{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6b3e5375",
   "metadata": {},
   "source": [
    "# 卷积神经网络\n",
    "\n",
    "在本章节的练习中，我们将对卷积，池化，空间批量归一化等内容进行编码，并比较不同实现方式的执行效率。由于python语言运行效率太过缓慢，我们会在第八章的tensorflow介绍章节再次训练卷积网络，本章我们只需要针对卷积网络的各个模块进行编码练习即可。本章我们将逐步完成：\n",
    "* 卷积前向传播编码练习\n",
    "* 卷积反向传播编码练习\n",
    "* 最大池化前向传播编码练习\n",
    "* 最大池化反向传播编码练习\n",
    "* 组合完整卷积层编码练习\n",
    "* 空间批量归一化编码练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b0b6d7d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "#-*- coding: utf-8 -*-\n",
    "import time\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from classifiers.chapter6 import *\n",
    "from utils import *\n",
    "\n",
    "%matplotlib inline\n",
    "plt.rcParams['figure.figsize'] =(10.0, 8.0) \n",
    "plt.rcParams['image.interpolation'] = 'nearest'\n",
    "plt.rcParams['image.cmap'] = 'gray'\n",
    "\n",
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "\n",
    "def rel_error(x, y):\n",
    "    \"\"\" 相对误差 \"\"\"\n",
    "    return np.max(np.abs(x - y) /(np.maximum(1e-8, np.abs(x) + np.abs(y))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "75fd9c67",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X_train:  (49000, 3, 32, 32)\n",
      "y_train:  (49000,)\n",
      "X_val:  (1000, 3, 32, 32)\n",
      "y_val:  (1000,)\n",
      "X_test:  (1000, 3, 32, 32)\n",
      "y_test:  (1000,)\n"
     ]
    }
   ],
   "source": [
    "# 导入数据\n",
    "data = get_CIFAR10_data()\n",
    "for k, v in data.items():\n",
    "    print('%s: ' % k, v.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26f19350",
   "metadata": {},
   "source": [
    "## 卷积前向传播(简单版本)\n",
    "\n",
    "卷积的前向传播过程，其实就是使用多个卷积核顺序扫描输入数据的过程。虽然思想简单，但对于实际编程而言可能就会比较有难度了。我们已经实现了前向传播最直观的版本，但执行效率十分低下，可读性也非常差。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "837b38fb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试 max_pool_backward_naive 函数:\n",
      "dx 误差:  3.275626262118968e-12\n"
     ]
    }
   ],
   "source": [
    "x = np.random.randn(3, 2, 8, 8)\n",
    "dout = np.random.randn(3, 2, 4, 4)\n",
    "pool_param = {'pool_height': 2, 'pool_width': 2, 'stride': 2}\n",
    "\n",
    "dx_num = eval_numerical_gradient_array(lambda x: max_pool_forward_naive(x, pool_param)[0], x, dout)\n",
    "\n",
    "out, cache = max_pool_forward_naive(x, pool_param)\n",
    "dx = max_pool_backward_naive(dout, cache)\n",
    "\n",
    "# 相对误差大约为 1e-12\n",
    "print('测试 max_pool_backward_naive 函数:')\n",
    "print('dx 误差: ', rel_error(dx, dx_num))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e646f4f5",
   "metadata": {},
   "source": [
    "由于 Python 自身执行速度缓慢的原因，即使我们完全向量化卷积后，该执行效率也远远达不到我们的需求。比较有效的方法是将完全向量版本的卷积操作使用 Cython 转化为 C 语言执行，但 Windows下需要安装诸多依赖库，操作不太方使。此处只进行完全向量版本的执行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf065141",
   "metadata": {},
   "source": [
    "## 快速卷积\n",
    "\n",
    "执行下列代码，比较快速卷积与你实现的卷积操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "274685c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试 conv_forward_fast:\n",
      "慢速版本: 4.569356s\n",
      "快速版本: 1.244883s\n",
      "加速: 3.670510x\n",
      "误差:  3.89496113023988e-11\n"
     ]
    }
   ],
   "source": [
    "from classifiers.chapter6.cnn_layers import conv_forward_fast\n",
    "from time import time\n",
    "\n",
    "x = np.random.randn(100, 3, 31, 31)\n",
    "w = np.random.randn(25, 3, 3, 3)\n",
    "b = np.random.randn(25,)\n",
    "dout = np.random.randn(100, 25, 16, 16)\n",
    "conv_param = {'stride': 2, 'pad': 1}\n",
    "\n",
    "t0 = time()\n",
    "out_naive, cache_naive = conv_forward_naive(x, w, b, conv_param)\n",
    "t1 = time()\n",
    "out_fast, cache_fast = conv_forward_fast(x, w, b, conv_param)\n",
    "t2 = time()\n",
    "\n",
    "print('测试 conv_forward_fast:')\n",
    "print('慢速版本: %fs' %(t1 - t0))\n",
    "print('快速版本: %fs' %(t2 - t1))\n",
    "print('加速: %fx' %((t1 - t0) /(t2 - t1)))\n",
    "print('误差: ', rel_error(out_naive, out_fast))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83c13527",
   "metadata": {},
   "source": [
    "## 快速池化操作\n",
    "\n",
    "执行下列代码，比较快速池化与你实现的池化操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "b7b9f0c9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试 pool_forward_fast:\n",
      "慢速版本: 0.009760s\n",
      "快速版本: 0.005861s\n",
      "加速: 1.665189x\n",
      "误差:  0.0\n",
      "\n",
      "测试 pool_backward_fast:\n",
      "慢速版本: 0.020494s\n",
      "快速版本: 0.014640s\n",
      "加速: 1.399909x\n",
      "dx 误差:  0.0\n"
     ]
    }
   ],
   "source": [
    "x = np.random.randn(100, 3, 32, 32)\n",
    "dout = np.random.randn(100, 3, 16, 16)\n",
    "pool_param = {'pool_height': 2, 'pool_width': 2, 'stride': 2}\n",
    "\n",
    "t0 = time()\n",
    "out_naive, cache_naive = max_pool_forward_naive(x, pool_param)\n",
    "t1 = time()\n",
    "out_fast, cache_fast = max_pool_forward_fast(x, pool_param)\n",
    "t2 = time()\n",
    "\n",
    "print('测试 pool_forward_fast:')\n",
    "print('慢速版本: %fs' %(t1 - t0))\n",
    "print('快速版本: %fs' %(t2 - t1))\n",
    "print('加速: %fx' %((t1 - t0) /(t2 - t1)))\n",
    "print('误差: ', rel_error(out_naive, out_fast))\n",
    "\n",
    "t0 = time()\n",
    "dx_naive = max_pool_backward_naive(dout, cache_naive)\n",
    "t1 = time()\n",
    "dx_fast = max_pool_backward_fast(dout, cache_fast)\n",
    "t2 = time()\n",
    "\n",
    "print('\\n测试 pool_backward_fast:')\n",
    "print('慢速版本: %fs' %(t1 - t0))\n",
    "print('快速版本: %fs' %(t2 - t1))\n",
    "print('加速: %fx' %((t1 - t0) /(t2 - t1)))\n",
    "print('dx 误差: ', rel_error(dx_naive, dx_fast))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d114306",
   "metadata": {},
   "source": [
    "# 完整的卷积层\n",
    "\n",
    "我们将卷积、ReLU和池化组合在一起，形成一层完整的卷积层。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "206e928b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试 conv_relu_pool\n",
      "dx 误差:  4.788779820806906e-08\n",
      "dw 误差:  1.4213812914502082e-09\n",
      "db 误差:  7.989753683822969e-11\n"
     ]
    }
   ],
   "source": [
    "x = np.random.randn(2, 3, 16, 16)\n",
    "w = np.random.randn(3, 3, 3, 3)\n",
    "b = np.random.randn(3,)\n",
    "dout = np.random.randn(2, 3, 8, 8)\n",
    "conv_param = {'stride': 1, 'pad': 1}\n",
    "pool_param = {'pool_height': 2, 'pool_width': 2, 'stride': 2}\n",
    "\n",
    "out, cache = conv_relu_pool_forward(x, w, b, conv_param, pool_param)\n",
    "dx, dw, db = conv_relu_pool_backward(dout, cache)\n",
    "\n",
    "dx_num = eval_numerical_gradient_array(lambda x: conv_relu_pool_forward(\n",
    "        x, w, b, conv_param, pool_param)[0], x, dout)\n",
    "dw_num = eval_numerical_gradient_array(lambda w: conv_relu_pool_forward(\n",
    "        x, w, b, conv_param, pool_param)[0], w, dout)\n",
    "db_num = eval_numerical_gradient_array(lambda b: conv_relu_pool_forward(\n",
    "        x, w, b, conv_param, pool_param)[0], b, dout)\n",
    "\n",
    "print('测试 conv_relu_pool')\n",
    "print('dx 误差: ', rel_error(dx_num, dx))\n",
    "print('dw 误差: ', rel_error(dw_num, dw))\n",
    "print('db 误差: ', rel_error(db_num, db))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "f40652b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试 conv_relu:\n",
      "dx 误差:  1.5225915734358116e-08\n",
      "dw 误差:  1.090045954938535e-09\n",
      "db 误差:  5.516659294279198e-12\n"
     ]
    }
   ],
   "source": [
    "# 生成测试数据\n",
    "x = np.random.randn(2, 3, 8, 8)\n",
    "w = np.random.randn(3, 3, 3, 3)\n",
    "b = np.random.randn(3,)\n",
    "dout = np.random.randn(2, 3, 8, 8)\n",
    "conv_param = {'stride': 1, 'pad': 1}\n",
    "\n",
    "out, cache = conv_relu_forward(x, w, b, conv_param)\n",
    "dx, dw, db = conv_relu_backward(dout, cache)\n",
    "\n",
    "dx_num = eval_numerical_gradient_array(lambda x: conv_relu_forward(x, w, b, conv_param)[0], x, dout)\n",
    "dw_num = eval_numerical_gradient_array(lambda w: conv_relu_forward(x, w, b, conv_param)[0], w, dout)\n",
    "db_num = eval_numerical_gradient_array(lambda b: conv_relu_forward(x, w, b, conv_param)[0], b, dout)\n",
    "\n",
    "print('测试 conv_relu:')\n",
    "print('dx 误差: ', rel_error(dx_num, dx))\n",
    "print('dw 误差: ', rel_error(dw_num, dw))\n",
    "print('db 误差: ', rel_error(db_num, db))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9391099e",
   "metadata": {},
   "source": [
    "## 浅层卷积网络\n",
    "\n",
    "接下来我们实现简单的浅层卷积网络，该网络由一层卷积层，两层全连接层组成：输入 - conv - relu - 2x2 max pool - affine - relu - affine - softmax。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da52451b",
   "metadata": {},
   "source": [
    "### 损失函数检验\n",
    "\n",
    "在不添加正则化的情况下，c分类任务，初始时的损失值应该接近于`log(c)`，运行下列代码进行损失值检验："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9d6a34a0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始损失值所对应分类(无正则化):  10.000002711556776\n",
      "初始损失值(正则化):  2.508280195836654\n"
     ]
    }
   ],
   "source": [
    "model = ThreeLayerConvNet()\n",
    "N = 50\n",
    "X = np.random.randn(N, 3, 32, 32)\n",
    "y = np.random.randint(10, size=N)\n",
    "\n",
    "loss, grads = model.loss(X, y)\n",
    "print('初始损失值所对应分类(无正则化): ', np.exp(loss))\n",
    "\n",
    "model.reg = 0.5\n",
    "loss, grads = model.loss(X, y)\n",
    "print('初始损失值(正则化): ', loss)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73b2e002",
   "metadata": {},
   "source": [
    "### 梯度检验\n",
    "\n",
    "运行下列代码进行梯度检验："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b7844df5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "W1 最大相对误差: 2.978425e-02\n",
      "W2 最大相对误差: 1.612577e-02\n",
      "W3 最大相对误差: 4.231643e-04\n",
      "b1 最大相对误差: 1.587488e-05\n",
      "b2 最大相对误差: 5.809691e-02\n",
      "b3 最大相对误差: 1.120641e-09\n"
     ]
    }
   ],
   "source": [
    "num_inputs = 2\n",
    "input_dim =(3, 16, 16)\n",
    "reg = 0.0\n",
    "num_classes = 10\n",
    "X = np.random.randn(num_inputs, *input_dim)\n",
    "y = np.random.randint(num_classes, size=num_inputs)\n",
    "\n",
    "model = ThreeLayerConvNet(num_filters=3, filter_size=3,\n",
    "                          input_dim=input_dim, hidden_dim=7)\n",
    "loss, grads = model.loss(X, y)\n",
    "for param_name in sorted(grads):\n",
    "    f = lambda _: model.loss(X, y)[0]\n",
    "    param_grad_num = eval_numerical_gradient(\n",
    "        f, model.params[param_name], verbose=False, h=1e-6)\n",
    "    e = rel_error(param_grad_num, grads[param_name])\n",
    "    print('%s 最大相对误差: %e' %(param_name, rel_error(\n",
    "            param_grad_num, grads[param_name])))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c41295c",
   "metadata": {},
   "source": [
    "### 过拟合小量数据\n",
    "\n",
    "运行下列代码，确保在小数据集上出现明显的过拟合现象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "66c4a314",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(迭代 1 / 40) 损失值: 2.388168\n",
      "(周期 0 / 20) 训练精度: 0.180000; 验证精度: 0.121000\n",
      "(周期 1 / 20) 训练精度: 0.130000; 验证精度: 0.105000\n",
      "(周期 2 / 20) 训练精度: 0.290000; 验证精度: 0.140000\n",
      "(迭代 6 / 40) 损失值: 2.912659\n",
      "(周期 3 / 20) 训练精度: 0.230000; 验证精度: 0.128000\n",
      "(周期 4 / 20) 训练精度: 0.280000; 验证精度: 0.127000\n",
      "(周期 5 / 20) 训练精度: 0.310000; 验证精度: 0.168000\n",
      "(迭代 11 / 40) 损失值: 1.985540\n",
      "(周期 6 / 20) 训练精度: 0.420000; 验证精度: 0.167000\n",
      "(周期 7 / 20) 训练精度: 0.460000; 验证精度: 0.177000\n",
      "(迭代 16 / 40) 损失值: 1.504879\n",
      "(周期 8 / 20) 训练精度: 0.550000; 验证精度: 0.186000\n",
      "(周期 9 / 20) 训练精度: 0.520000; 验证精度: 0.174000\n",
      "(周期 10 / 20) 训练精度: 0.640000; 验证精度: 0.194000\n",
      "(迭代 21 / 40) 损失值: 1.109133\n",
      "(周期 11 / 20) 训练精度: 0.700000; 验证精度: 0.193000\n",
      "(周期 12 / 20) 训练精度: 0.720000; 验证精度: 0.187000\n",
      "(迭代 26 / 40) 损失值: 0.931523\n",
      "(周期 13 / 20) 训练精度: 0.750000; 验证精度: 0.225000\n",
      "(周期 14 / 20) 训练精度: 0.800000; 验证精度: 0.225000\n",
      "(周期 15 / 20) 训练精度: 0.800000; 验证精度: 0.209000\n",
      "(迭代 31 / 40) 损失值: 0.650131\n",
      "(周期 16 / 20) 训练精度: 0.860000; 验证精度: 0.211000\n",
      "(周期 17 / 20) 训练精度: 0.930000; 验证精度: 0.237000\n",
      "(迭代 36 / 40) 损失值: 0.243213\n",
      "(周期 18 / 20) 训练精度: 0.920000; 验证精度: 0.238000\n",
      "(周期 19 / 20) 训练精度: 0.940000; 验证精度: 0.228000\n",
      "(周期 20 / 20) 训练精度: 0.960000; 验证精度: 0.218000\n"
     ]
    }
   ],
   "source": [
    "num_train = 100\n",
    "small_data = {\n",
    "  'X_train': data['X_train'][:num_train],\n",
    "  'y_train': data['y_train'][:num_train],\n",
    "  'X_val': data['X_val'],\n",
    "  'y_val': data['y_val'],\n",
    "}\n",
    "\n",
    "model = ThreeLayerConvNet(weight_scale=1e-2)\n",
    "\n",
    "trainer = Trainer(model, small_data,\n",
    "                num_epochs=20, batch_size=50,\n",
    "                update_rule='adam',\n",
    "                updater_config={\n",
    "                  'learning_rate': 1e-3,\n",
    "                },\n",
    "                verbose=True, print_every=5)\n",
    "trainer.train()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "13f7fec2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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llGQl8uUSaM4e2xhusMSERERyVhKWlvZjDnF3Dh9PmWV1QAUl5Zx4/T5AEpcRUREROJQeUArmzpz8Z6EtVZZZTVTZy5OU0QiIiIimU9JaysrKS1r0nIRERERUdLa6grycpu0XERERESUtLa6KZNGkZuTXWdZbk42UyaNSlNEIiIiIplPN2K1stqbrTR6gIiIiEjilLSmwdljC5WkioiIiDSBygNEREREJOMpaRURERGRjKekVUREREQynpJWEREREcl4beJGLDNbAWwHqoEqdx+X3ohEREREpDW1iaQ1dJy7b0p3ECIiIiLS+lQeICIiIiIZr60krQ48b2azzeyqdAcjIiIiIq2rrZQHTHT3EjMbALxgZh+5+2u1K8NE9iqAYcOGpStGEREREUmRNtHT6u4l4fMG4AngiKj109x9nLuP69+/fzpCFBEREZEUyvik1cy6m1nP2tfAycCC9EYlIiIiIq2pLZQHDASeMDMI4v2bu/87vSGlz4w5xUyduZiS0jIK8nKZMmkUZ48tTHdYIiIiIimV8Umruy8DRqc7jkwwY04xN06fT1llNQDFpWXcOH0+gBJXERERadcyvjxA9po6c/GehLVWWWU1U2cuTlNEIiIiIq1DSWsbUlJa1qTlIiIiIu2FktY2pCAvt0nLRURERNoLJa1tyJRJo8jNya6zLDcnmymTRqUpIhEREZHWkfE3YsletTdbafQAERER6WiUtLYxZ48tVJIqIiIiHY7KA0REREQk4ylpFREREZGMp6RVRERERDKeklYRERERyXhKWkVEREQk4ylpFREREZGMp6RVRERERDKeklYRERERyXhKWkVEREQk4ylpFREREZGMp6RVRERERDKeklYRERERyXhKWkVEREQk4ylpFREREZGMp6RVRERERDKeklYRERERyXhKWkVEREQk4ylpFREREZGM1yaSVjPLNrM5ZvZMumMRERERkdbXJpJW4NvAonQHISIiIiLpkfFJq5kNAU4H7kp3LCIiIiKSHhmftAK/A74H1MRrYGZXmdksM5u1cePGVgtMRERERFpHRietZnYGsMHdZzfUzt2nufs4dx/Xv3//VopORERERFpLRietwETgTDNbAfwdON7MHkpvSCIiIiLS2jI6aXX3G919iLsXAZOBl9z94jSHJSIiIiKtLKOTVhERERERgE7pDiBR7v4K8EqawxARERGRNFBPq4iIiIhkPCWtIiIiIpLxlLSKiIiISMZT0ioiIiIiGU9Jq4iIiIhkPCWtIiIiIpLxlLSKiIiISMZT0ioiIiIiGU9Jq4iIiIhkPCWtIiIiIpLxlLSKiIiISMZT0ioiIiIiGU9Jq4iIiIhkvE7pDkAy34w5xUyduZiS0jIK8nKZMmkUZ48tTHdYIiIi0oEoaZUGzZhTzI3T51NWWQ1AcWkZN06fD6DEVURERFqNygOkQVNnLt6TsNYqq6xm6szFaYpIREREOiIlrdKgktKyJi0XERERSQUlrdKggrzcJi0XERERSQUlrdKgKZNGkZuTXWdZbk42UyaNSlNEIiIi0hHpRixpUO3NVho9QERERNJJSas06uyxhUpSRUREJK1SVh5gZkeY2deilp1lZvPNrNjMfpGqfYuIiIhI+5LKmtYfA2fWvjGzYcAjwCBgK/B9M7sihfsXERERkXYilUnraOCNiPeTAQPGuPtBwPPAVY1txMy6mtm7ZjbPzD40s5+kJlwRERERyVSpTFrzgXUR7ycBr7l7cfj+KWBkAtvZDRzv7qOBMcApZjYhmYGKiIiISGZL5Y1YpcBAADPrAkwAIutYHWh0sE93d2BH+DYnfHgyA+0IZswp1ggAIiIi0malMmmdC3zVzF4EzgG6AjMj1o8A1ieyITPLBmYD+wF/cvd3khtq+zZjTjE3Tp+/ZzrW4tIybpw+H0CJq4iIiLQJqSwPuAUYDLwL/AB40d1nRaw/A0go+XT3ancfAwwBjjCzQyLXm9lVZjbLzGZt3LgxKcG3J1NnLt6TsNYqq6xm6szFKd/3jDnFTLztJUbc8CwTb3uJGXOKG/+QiIiISJSU9bS6+5tm9hmCWtatwN9r15lZPsGNWE80cZulZvYKcAqwIGL5NGAawLhx41Q6EKWktKxJy5NFPbwiIiKSLCmdxtXdl7j7H9z9AXeviFi+2d2vd/fXGtuGmfU3s7zwdS5wIvBRyoJuhwryYpcOx1ueLOns4RUREZH2JZWTC2SbWbeoZXlm9j9m9vPoS/wNGAy8bGYfAO8BL7j7M8mOtz2bMmkUuTnZdZbl5mQzZdKolO43XT28IiIi0v6k8kasvxCMGHAIgJnlAK8DB4Xrv2tmR7n73IY24u4fAGNTGGe7V3spvrVHDyjIy6U4RoKa6h5eERERaX9SmbR+Fpge8f48goT1m8AcghrXGwgmHZAUO3tsYavXkU6ZNKpOTSu0Tg+viIiItD+pTFoHA8sj3p8OfOjufwYws2nA11O4f0mzdPXwioiISPuTyqTVgMhCymOp2/O6FhiQwv1LBkhHD6+IiIi0P6kcPWA5wXBXmNlEwhuqItYXEAyFJSIiIiLSoFT2tN4L/MbMFgCFwAbqzoh1JBq6SkREREQSkLKeVnf/HfBjYDfBjVfnuPsu2DO5wATguVTtX0RERETaj1T2tOLutxBM5xq9fDOqZxURERGRBKV0RqxIZtbPzPq11v5EREREpP1IadJqZgVmdr+ZlQLrgfVm9qmZ3WdmuqVcRERERBKSsvIAMxsGvA0MAuYCH4arDgIuBU4yswnuvjpVMYiIiIhI+5DKmtZbgD7AGe5e54YrMzuVYMzWW4DLUxiDiIiIiLQDqSwPOBm4IzphBXD3fwF/Bk5J4f5FREREpJ1IZU9rH+DjBtZ/DOSlcP8pN2NOsaYoTREdWxEREYmUyp7WNQRTt8bz+bBNmzRjTjE3Tp9PcWkZDhSXlnHj9PnMmFOc7tDaPB1bERERiZbKpPWfwJfM7FYz61270Mx6mdkvgPOBR1O4/5SaOnMxZZXVdZaVVVYzdebiNEXUfujYioiISLRU34j1OeD7wP+aWUm4vADIBt4AfpbC/adUSWlZk5ZL4lp6bFVaICIi0v6kchrXXcAxwNeBF4CdwC5gJnAVcJy7t9kMryAvt0nLJXEtObYqLRAREWmfUjq5gLtXu/tf3f00dz/I3Q909zPc/S53r0rlvlNtyqRR5OZk11mWm5PNlEmj0hRR+9GSY6vSAhERkfYpaeUBZnZpcz7n7g8kK4bWVHu5WZehk68lx1ZlGyIiIu1TMmta7wMcsCZ8xoE2mbRCkFwpSU2N5h7bgrxcimMkqCrbEBERaduSmbQel8RtiTTLlEmjuHH6/DolAirbEBERafuSlrS6+6vJ2pZIc6lsQ0REpH1K5ZBXImmhsg0REZH2J6WjB4iIiIiIJEPG97Sa2VCCm7UGATXANHe/Pb1RiaSfJlEQEZGOJOOTVqAK+B93f9/MegKzzewFd1+Y7sCkfUlXEtic/dZOolB7w1ntJAqAElcREWmXMr48wN3Xuvv74evtwCJAf5UlqdI1k1Zz96tJFEREpKPJ+KQ1kpkVAWOBd9IcirQzLU0CZ8wpZuJtLzHihmeZeNtLCSe7zd2vJlEQEZGOps0krWbWA3gc+I67b4tad5WZzTKzWRs3bkxPgNKmtSQJbEkvbXP3G2+yBE2iICIi7VWbSFrNLIcgYX3Y3adHr3f3ae4+zt3H9e/fv/UDlDavJUlgS3ppm7vfKZNGkZuTXWdZUyZRaG7PsIiISLpkfNJqZgbcDSxy99+kOx5pn1qSBLakl7a5+z17bCG3nnsohXm5GFCYl8ut5x6a0E1Y6arfFRERaYm2MHrAROASYL6ZzQ2X/cDdn0tfSNLetGQmrYK8XIpjJKiJ9NK2ZL/NnUShoZ5hjTwgIiKZKuOTVnd/HbB0xyHtX3OTwCmTRtUZfgqadqm+tWfw0k1cIiLSFmV8eYBIpmvJpfp00E1cIiLSFmV8T6tIW9DavaUt0dKeYRERkXRQ0irSwbSkjlZERCRdlLSKdEBtqWdYREQElLSKSBPMmFOsHloREUkLJa0ikpDa8V1ra2Frx3cFEh4fVgmviIg0l0YPEJGEtGTmL01oICIiLaWeVhFJSEvGd9WEBolRb7SISHxKWkUkIS2Z+aulExqkI5lr7X22tPxCRKS9U3mAiCRkyqRR5OZk11mW6PiuLZnQoCWlBTPmFDPxtpcYccOzTLztpYTLEdJRztCS8gsRkY5ASauIJKQlM3+1JOFtbjLXksQzHQmkptcVEWmYygNEJGHNHd+1JRMaNDeZa0kdbUsSyOaWFbSk/EJEpCNQ0ioiraK5CW9zk7mWJJ7N3WdL6lI1va6ISMNUHiAiGa25pQUtqaNt7j5bUlbQkvILEZGOQD2tIpLRmlta0JKey+bus6V1qZpeV0QkPiWtIpLxmpPMtaSOtrn7VF2qiEjqKGkVkXartXsuVZcqIpI6SlpFRJKkpb27IiISn5JWEZEkUl2qiEhqaPQAEREREcl4SlpFREREJOOpPEBEpB1o7kxcIiJthZJWEZE2riUzcYmItBUZXx5gZveY2QYzW5DuWEREMlFLZuJqiRlzipl420uMuOFZJt72EjPmFKd0fyLSsWV80grcB5yS7iBERDJVS2fiao7a3t3i0jKcvb27SlxFJFUyPml199eALemOQ0QkU8WbcSuRmbia21uart5dEem4Mj5pFRGRhk2ZNIrcnOw6yxKZiaslvaXp6N1tKZUziLRt7SJpNbOrzGyWmc3auHFjusMREWlVZ48t5NZzD6UwLxcDCvNyufXcQxu9CaslvaUt6d1NB5UziLR97WL0AHefBkwDGDdunKc5HBGRVtecmbha0ls6ZdKoOiMWQGK9u+nSUIKuERZE2oZ20dMqIiJN15Le0ub27qZLWyxnEJG6Mr6n1cweAY4F+pnZGuDH7n53eqMSEWn7Wtpb2pze3XQpyMulOEaCmqnlDCJSX8Ynre5+QbpjEBFpj2oTzo4wk1ZLEnTNNiaSGTI+aRURkdRpS72lLdHcBF2zjYlkDiWtIiLS6tLRe9mcBF03cIlkDiWtIiLSqlrSe9naya5u4BLJHBo9QEREWlVzx4dNx1ir6RyPVpMhiNSlpFVERFpVc3sv0zF1bHNnG2updE2GoERZMpnKA0REpFU1d/ipdFyqb+kIC80tZ0hHLa1uOpNMp6RVRERaVXOHn0rXWKvNHWGhJUlgOhJ03XQmmU7lASIi0qqaO5tWui7VN1dLyhnSUUubrpvOVJIgiVJPq4iItLrm9F62tckQWpIEtnS2suZIR0+2ShKkKZS0iohIm9GWJkNoSRLYkgS9uXW06UiUVZIgTaGkVUREJAVamgQ2J0FvSc9lOhJljYObGE0lHFDSKiIikgLpKGdoac9layfK6bq5ri1RCcVeSlpFRERSpLXLGdraqAPpKEloa1RCsZdGDxAREWkn2tqoA80dSaIjUQnFXuppFRERaSfa4qgDLemN7gi1niqh2Es9rSIiIu1EOnouO9pUt62trY1PnErqaRUREWlHWruONl3j53aUWs+2Nj5xKilpFRERkRZJx/i56ZzBq7UTyLY0PnEqqTxARERE2px03HTWUUoSMpWSVhEREWlz0lHr2VBJgqSeygNERESkzWlrM3ila6SD5u43E0dmUNIqIiIibVJbmcErXbNaNXe/mToLl8oDREREpMNoySX+5pYkpKusoLn7zdQyCPW0ioiISIfR0hm8oOklCeka6aC5+83UWbgyPmk1s1OA24Fs4C53vy3NIYmIiEgblY4ZvNI1q1Vz95ups3BldHmAmWUDfwJOBQ4CLjCzg9IblYiIiLRV6Rh1IF2zWjV3v5k6C1em97QeAXzi7ssAzOzvwFnAwrRGJSIiIm1SOmaYStesVs3db6bOwmXuntYAGmJm5wGnuPtXw/eXAEe6+7XxPjNu3DifNWtWa4UoIiIiIklkZrPdfVz08owuDwAsxrJ6WbaZXWVms8xs1saNG1shLBERERFpTZmetK4Bhka8HwKURDdy92nuPs7dx/Xv37/VghMRERGR1pHpSet7wEgzG2FmnYHJwFNpjklEREREWllG34jl7lVmdi0wk2DIq3vc/cM0hyUiIiIirSyjb8RqDjPbCKxs5d32Aza18j7bGh2jxukYNU7HqHE6Ro3TMWqcjlHjdIwa19xjNNzd69V7trukNR3MbFasu9xkLx2jxukYNU7HqHE6Ro3TMWqcjlHjdIwal+xjlOk1rSIiIiIiSlpFREREJPMpaU2OaekOoA3QMWqcjlHjdIwap2PUOB2jxukYNU7HqHFJPUaqaRURERGRjKeeVhERERHJeEpaW8DMTjGzxWb2iZndkO54MpGZrTCz+WY218xmpTueTGBm95jZBjNbELGsr5m9YGYfh8990hljusU5RjebWXF4Ls01s9PSGWO6mdlQM3vZzBaZ2Ydm9u1wuc6lUAPHSOdSyMy6mtm7ZjYvPEY/CZfrPAo1cIx0HkUxs2wzm2Nmz4Tvk3oeqTygmcwsG1gCnEQw3ex7wAXuvjCtgWUYM1sBjHN3jWUXMrPPAzuAB9z9kHDZr4At7n5b+B+gPu7+/XTGmU5xjtHNwA53/3/pjC1TmNlgYLC7v29mPYHZwNnA5ehcAho8RuejcwkAMzOgu7vvMLMc4HXg28C56DwCGjxGp6DzqA4z+y4wDujl7mck+2+belqb7wjgE3df5u4VwN+Bs9Ick7QB7v4asCVq8VnA/eHr+wn+sHZYcY6RRHD3te7+fvh6O7AIKETn0h4NHCMJeWBH+DYnfDg6j/Zo4BhJBDMbApwO3BWxOKnnkZLW5isEVke8X4N+GcbiwPNmNtvMrkp3MBlsoLuvheAPLTAgzfFkqmvN7IOwfKDDXq6MZmZFwFjgHXQuxRR1jEDn0h7hJd25wAbgBXfXeRQlzjECnUeRfgd8D6iJWJbU80hJa/NZjGX6n1d9E939M8CpwDfDy74izfFnYF9gDLAW+HVao8kQZtYDeBz4jrtvS3c8mSjGMdK5FMHdq919DDAEOMLMDklzSBknzjHSeRQyszOADe4+O5X7UdLafGuAoRHvhwAlaYolY7l7Sfi8AXiCoKxC6lsf1t/V1uFtSHM8Gcfd14d/OGqAv6JzibC+7nHgYXefHi7WuRQh1jHSuRSbu5cCrxDUauo8iiHyGOk8qmMicGZ4H8vfgePN7CGSfB4paW2+94CRZjbCzDoDk4Gn0hxTRjGz7uHND5hZd+BkYEHDn+qwngIuC19fBjyZxlgyUu0vvtA5dPBzKbw55G5gkbv/JmKVzqVQvGOkc2kvM+tvZnnh61zgROAjdB7tEe8Y6Tzay91vdPch7l5EkA+95O4Xk+TzqFOLouzA3L3KzK4FZgLZwD3u/mGaw8o0A4Engr8bdAL+5u7/Tm9I6WdmjwDHAv3MbA3wY+A24B9mdiWwCvhS+iJMvzjH6FgzG0NQhrMC+Hq64ssQE4FLgPlhrR3AD9C5FCneMbpA59Ieg4H7wxFxsoB/uPszZvYWOo9qxTtGD+o8alRSfx9pyCsRERERyXgqDxARERGRjKekVUREREQynpJWEREREcl4SlpFREREJOMpaRURERGRjKekVUSkAWZ2rJm5mV2e7liaw8wuD+M/Nt2xiIi0hJJWEZEmMLMiM7s5HJ8xI4SJ9c21A6CLiLRHGqdVRKQBZpYFdAYq3b067LF8GbjC3e9LY2h7mNnNBBMwjHD3FVHrsoEcoCKcblJEpE3SjFgiIg0IE73y1tqfmfV09+3J2p67VwPVydqeiEi6qDxARKQBkTWtYV3ry+Gqe8PlbmavRLQ3M/uGmc02s11mtt3MXjaz46K2WxR+9mYz+3LYvgz4Q7j+ADO7w8w+DLexK2zztajt3EfQywqwPCKmm8P1MWtazayfmf3JzFabWUX4/Cczy49qV/v5483sf81sqZntNrMlZnYZIiKtRD2tIiKJew34BcH89dOA/4bL10e0eRC4AHgMuBfoAlwEvGBm57r7U1HbPBv4FvBn4E5gW7j8WODzwDPAcqA7wbzd08ysn7vfGrb7C9ALOAe4HtgULv8g3pcws97Am8B+wD3A+8BY4BvA8WZ2RIze3l8AueH+dodt7zOzT9z9jXj7EhFJFiWtIiIJcvdlZvYCQdL6lrs/FLnezM4hSFC/7u7TIpbfDrwN3G5mT3vdmwkOBg5z90VRu3vQ3e+M2v5vgZeAG8zs/7l7pbu/ZWYfECStM6JrWuP4HjAS+Ka73xGx/bnAH8P1P4r6TBdgvLtXhG0fA5YB1wJKWkUk5VQeICKSPBcD24EZ4eX3fmbWD8gDngaKCJLFSM/GSFhx9521r82sa3jZvi/wPEHP6gEtiPMcYCNBb3GkvxD01J4T4zN31CasYXzFwBLqfx8RkZRQT6uISPIcCPSkbrlAtIEEyV6tJbEamVkP4GbgfGBojCZ9mhciACOAWe5eFbnQ3avMbDHwmRifWRZj2WZgeAviEBFJmJJWEZHkMYIezAsbaLMg6v2uOO3+BpxB0Bv6GrAFqAJOI6hdbe0rZfFGILBWjUJEOiwlrSIiTdPQ4NYfA/sDb7v7jubuIJwk4AyCutaro9ad2MSYYlkGjDKzTpG9rWbWiSD+WL2qIiJppZpWEZGmqU1G+8ZY9wDB79VbY6zDzAYmuI/aXs06vZhmNhj4ahNjimUG0D/Gtr4WLn8iwe2IiLQa9bSKiDTNQoKbra4xs11AKbDB3V9y98fM7F7gWjP7DMFwVZuAIcBRBENM7dPYDtx9u5k9D1wcjt36HkHt6NcJhr/Kj/rI2+HzL83sYYLJEBa4e3QpQq1fEQyf9acwzjkEQ15dCSwO14uIZBT1tIqINIG7lwGTCcZT/R3wCPB/Eeu/AlwK1AA3EkwWcBlBb+iNTdjVxQRjqH6BYBiqs4EfAn+KEdMbwPeBfYG/hjGd18B32ApMJBgt4DTg9+HzncBnkzkjl4hIsljd4QJFRERERDKPelpFREREJOMpaRXpYMzsPjPTJRZJGzNzM7sv3XGISNuipFUkQ5jZGDO72cyK0h2LZA4z+2eY5I1poI2Z2XIzKzWz3HBZVzO7zszeM7NNZrbLzFaa2b/N7Put9gVERJJESatI5hgD/Jhgqs9U+hqQm+J9SPLcHT5f0UCb4wjOm7+7e1k43up/CG6w2gD8HPg28DDQneCGLhGRNkVDXom0UWaWDXRx93gzKsXk7pVAZWqiapvMzIDuLZkQIIWeB1YDF5nZFHeviNGmNqGtTXDPAo4Gfufu10c3NrMhKYlU6jCznhqJQSR51NMqkgHM7Gbg3vDty+Hl4D11f2Z2efj+RDP7kZktJRiL8/xw/clm9qiZLTOzsvAy8fNmdkyMfdWraa1dZma9zezPZrbBzMrN7A0zOzLB71BgZr82s7lm9mn4+YVm9v0wwY5u39nMvhe232VmW81slpldG9Wul5n93MwWhdvcbGavm9nkiDavmNmKGPsoCr/XzRHLjg2XXW5m3zSzheGx/N9w/RHh8VgSxrU9PA7nxPneg8zs9+Gx3x0euxfM7KRw/VNmttPMesX47BFhLD+Kd1zdvQa4j2Bs1jNjbKMXcC7BuKzvhYtHhs//ibPNNfH2F7Hdd8xsfdhrG71uUhj3d8L3WWb2QzN7zczWmVmFma0Kz6XoMWUT1pztmtkXzezl8Gdgl5ktDv99Oke0MTP7Wvgdd4SP+Wb204g2N4ffsSjGPlaY2StRyzw8b04Iz88dwNPhuqT+bJjZd8P91Zsdzcy6mNkWM4v5by/SlqmnVSQzTAcGA1cBvwAWhcuXRrX7f0AOwVic2wgGgge4nGA2pAeANUAhwWxH/zGz49z9vwnGMRPYCPyUIEn6LvCcmRUl0GN0GEHy9EQYdw5wKnAbwYD6X69tGCYQM4FjCXoSHyJIHA8Nt/HHsF0e8DpwMPAY8Gcgm2Ag/DOAvyf4vWL5Tvgd/wqsI+jNBDgHOAD4B7AybHMZMN3MLnL3v0V8jyLgDWAgwbGfRXD5fQJwIvACMI1grNULCMZFjfQVgvFc72sk1nuBmwh6VB+LWjcZ6MbeXlbYe95cbGb/CceWbar7CcaEPYVgkoRIlwJVQO2x6AxMAR4HngR2AuMJJiv4rJkdHqeHuDFN2q6Z/Rz4AcEEEL8F1hKMXftFgrF0a9s+CFwEvENQOlFK8G9+HhFj7jbDuHBffyU4frWS/bNxP8HviSuBF6NiOAfoQ93zQaR9cHc99NAjAx4EiacDxzawbjHQLcb67jGWDSSYjem5qOX3BT/69ZcBd0Qt/1K4/OsJxJ9LOPZz1PIHCaYlHRyx7Hvhdn8Ro31WxOs7wnZXNdLuFWBFjDZF4edvjlh2bLhsCzAgwWPZLTz2C6OWPxdua1K8+AiS7FXAuzG2uTX636eB4/sfgkSxIGr5W8BuoF/Ess7A7DC2UoKk80cEiXROgvvrG273H1HLexIkj09FLDMgN8Y2rgxjOD9quQP3JRBDwtsFjgiXvQR0jbGd2nHJzw/bPRh5DsU4p24O2xXF2P8K4JUY38mBE1vpZ+NvBMls36g2L4Tndtfoz+uhR1t/qDxApG35s8eoYXX3nbWvzaxHeOm0mqAnKaHL+6HfRr1/KXweGd0wRgxl7u5hDJ3NrK+Z9SPoNcoi6IWqdRHwKUGPbvR2asJtZBH0Ii4i6LmK2a4FHnD3DTG2G3ksu4XHshvBsTiw9jK/mfUl6IX8t7vPjBefu1cTzGw13swOjWhyHtCLxHvE7iZIgC+JiO8Agl7dp9x9U8S+K4BjCHpnVxLMdvVTgoRmjZld1NjO3H0LweXtM8Me78i4uxHRk+iBsjCmbDPLC//ta8+fppyDkTE0Zbu13+lGdy+PsR2Pave/0edQEs6pee4e3fOZ9J+N0DSgC3u/T23P/wnAw9HHQKQ9UNIq0rYsibXQzPY1s7+b2afAdoIe1o0EyUqfJmx/WeQbd98cvmy0LtHMOpnZTWa2hKAHaHMYw4Nhk8g4RgIfNfKHtV/4mbkRCUcyxTuWA8xsmpmtJ+hRrD2WV4dN8sLn/Qh68OYksK+7Cf4TcWXEsisJ7ux/KsF4pxP0mkaOIvCV8Pme6MbuvsPdf+7uo8OYTyK43N8HeMDMJiawzwcIEqPzI5ZdSpBU1SkZMLPzzewdoCxcv5G951NTzsE6mrDdkQQ9lPMa2eRIYK27r29uTA2Id04l+2cDd38l3F/kOXUFwTl5V7OiF8lwSlpF2pZ6vaxm1gN4jaDX73aCnrBJBEnKSwR/xBIS9grGksg2fgPcArxP8MfztDCG2jFBo3/fNJaI1u4zkYQ1XpuG6vZjHUsjqCO8jCBh+zLBcT2JvfWbtd8j4fjcfTXwb4Ia085mth/weYLe3oRGcgiTmL8Bo8zs6PAGnksIapifb+Sz29z9RXe/Fvhm+B0aGkKr1nMEydWlAGY2jKAH9+/uvru2kZmdCzwavv02QQ3vSQTHDpr5t6aJ2zUSO1cSbddQm3jnVbyRPJL9s1Hrr8BoMzs8vDJxOTDL3RtL3EXaJN2IJZI5mtubeAJQAHzF3e+NXGFmP2txVIm7BHjN3SdHLgwTtGhLCC61d4lMfqJsJOhZG5PAvrcAh8dYvk8Cn410GDAa+Km7/zhyhZl9NartxwT/ZmMT3PY04HTg7IjPNPVmmbuBawgSn77AIODnDfxnI5a3w+fCxhq6e5WZ/Q34tpntQ3AzmVH3JiMI/u3LgeMiy1fC8oWWaMp2FxMks4cB7zawzcXAWWY2sJHe1i3hc1+CGtbafXcluGnyk0S+QCjZPxu17iO4kexKghvVhgG3NiEukTZFPa0imaN2jNC+TfxcbcJSpzfUzE6mmbWEzVQdI4buQL1xQgkGue9DUHNZR9jbWVu/9whwkJldGa9daAnQ08yOiFifFWffjX0HqP89DiG4K3uPsObzX8CpcYYeiu6dfhYoJrhT/DLgDXf/qCnBufv7wFyCHuBrCZLme6PbWTC72uA4mzk7fF6Y4G5rE9RLCZKvxe7+TlSb6jCWPX9Twu9f79+3iZqy3dqe8F+YWZfolRH/Hg+Hz78Kz5FYbWDvpf7of9vrafrfzqT+bNQK65hnABcSnA+72HscRNod9bSKZI73CIY/+qGZ9SGop1weI0GI9jrBkE2/Dm/EWEPQO3kJMJ9gqJzW8BjwdTN7lGAYnoEENZebY7S9neBS701mNp7g8nY5wdBWo9ibKNwEHA/cFSbhrxP88R9L8Pur9qakacD/AE+Y2e0EQxudR9N/xy0CPgS+Z2a1IwbsT5BoLgA+E9X+WuBN4F9mdj/BHfu5BP9ZWMHey7+4e7WZ1Q5dBcHQTM1xN/AHghKQV9w9elg0CI7fL8zseYIhudYBvQlGTjiTYCio3ySyM3efY2bzCRKsXnHifoxgqKeXzOwBgiGdzia4YaslEt6uu79rZr8kOOazw/NwHTCC4Fw4Aih193+G6y4FRprZUwQ9+vsTHNNDwk2+CHwE/DS8GW858FmCG9820TSp+NmoNY2g5vgM4H5339bE2ETajnQPX6CHHnrsfRD0wC0kSLr2DAtEA8NhhesPI6iZrL0R6xXgczQwvFVjyyLWJTo8UTdgKsHd6uUEl89vIChfcODyqPZdCaYT/TBsX0qQuF8T1S4P+BXB5dgKgj/0/6X+MEqnEfRC7gZKgF8S/JGPN+TV5XG+x3DgnwTlCbsILjWfQ5whkAgus99JMKxVBbCeINE4Ic62qwnG2K03tFaC50gfgpuSHLgkTpui8Ni+TDD+7G6C/wR9CPwaGNTEff5PuL9qYGicNl8Lz91ygqR4GsFVg3rnT6LnVFO3G7a/gCBR3x5+54+A3wGdI9pkEdT2vh/+G28HPgB+HLWt/Ql+rnaF5+c/wn/vFcQe8irmdyJFPxthW2NvqcrnmnNO6aFHW3nUjlsnIiIpFl6yXw3c7e5fb6y9SCLM7EMg291bWkMsktFU0yoi0nq+QTDW6rR0ByLtg5kdDxyEzinpANTTKiKSYmY2meDO7p8Ar7r7KY18RKRBYbK6L3Aj0APY1xufalmkTUtb0mpm9xAUjm9w90NirDeCgvTTCOqJLvfgzlkRkTbFzJygNvG/wBXuXpzmkKSNM7NXCG4MWwh8093/m96IRFIvnUnr5wmG+HkgTtJ6GnAdQdJ6JHC7u7fm8D0iIiIikiHSVtPq7q+xd/DmWM4iSGjd3d8G8hoYd1BERERE2rFMHqe1kOAu21prwmVroxua2VXAVQDdu3c//IADdAOliIiISFs0e/bsTe7eP3p5JietseY6j1nL4O7TCO+cHDdunM+aNSuVcYmIiIhIipjZyljLM3nIqzXA0Ij3QwgGDBcRERGRDiaTk9angEstMAHY6u71SgNEREREpP1LW3mAmT1CMJ1iPzNbA/yYYF5p3P1O4DmCkQM+IRjy6or0RCoiIiIi6Za2pNXdL2hkvRPMDd1ilZWVrFmzhvLy8mRsLqN17dqVIUOGkJOTk+5QREREpI2aMaeYqTMXU1JaRkFeLlMmjeLssYVpjSmTb8RKmjVr1tCzZ0+KiooI5ixon9ydzZs3s2bNGkaMGJHucERERKQNmjGnmBunz6esshqA4tIybpw+HyCtiWsm17QmTXl5Ofn5+e06YQUwM/Lz8ztEj7KIiIikxi///dGehLVWWWU1U2cuTlNEgQ7R0wq0+4S1Vkf5niIiItI87s7mnRWs3LyTFZt2Bc+b9z5vLauM+bmS0rJWjrSuDpO0plNpaSl/+9vfuOaaa5r0udNOO42//e1v5OXlpSYwERERaROaWmPq7mzcvpsVm3exYvPOOonpyk272L67ak/bLIPCPrkU5XfnzNEFPDWvmK1lVfW2WZCXm5LvliglrTEku/i4tLSUO+64o17SWl1dTXZ2dtzPPffcc83ep4iIiLQP8WpMvcaZsF9+zN7SlZt3sqti7yX+7CxjaJ9civp1Z9zwvgzP70ZRfneG53djSJ9udO60t2L08OF96uwPIDcnmymTRrXel45BSWuUVBQf33DDDSxdupQxY8aQk5NDjx49GDx4MHPnzmXhwoWcffbZrF69mvLycr797W9z1VVXAVBUVMSsWbPYsWMHp556Kp/97Gd58803KSws5MknnyQ3N73/4xEREckEmXine7LU1Di3/mtRzBrT6/85r86ynGxjaN9ujMjvzlH75FPUrxvD87tTlN+NgrxccrITu5Wp9thl2jG1YGSp9iPWNK6LFi3iwAMPBOAnT3/IwpJtcT8/Z1UpFdU19ZZ3zs5i7LC8mJ85qKAXP/7CwXG3uWLFCs444wwWLFjAK6+8wumnn86CBQv23OG/ZcsW+vbtS1lZGePHj+fVV18lPz+/TtK63377MWvWLMaMGcP555/PmWeeycUXXxxzf5HfV0REpD2L7myCoFfw1nMPTXuSlaiq6hrWbi1n+aad9XpMV23ZRUVV/byk1s/OPmRPj2lBXi7ZWW3/3hYzm+3u46KXq6c1SqyEtaHlzXHEEUfUGZLq97//PU888QQAq1ev5uOPPyY/P7/OZ0aMGMGYMWMAOPzww1mxYkXS4hEREWmrps5cHLMX8tZ/LeLYUf3p2TUn6Ylcc3p2K6trKP60LKwv3bUnQV25eRerP91FZfXeTsSuOVkU5Xdn3/7dOeHAATz67mpKY9wcVZiXy8UThif1u2WyDpe0NtQjCjDxtpcojnF3XGFeLo9+/aikxNC9e/c9r1955RVefPFF3nrrLbp168axxx4bc8iqLl267HmdnZ1NWVl67+ATERFJt4qqmph/swHWb9vNmJ++gBn06ppDn2459O7WmbzcHPK65ZCXG/W+Ww55e953plfXTnSKcTm9oTLC0w4dzOpPd9W5K3952Gu65tMyqmv2JqbdO2czPL87BwzuyaRDBjEi7C0t6tedAT271BkN6MBBvTKyxrS1dbiktTFTJo1K+onRs2dPtm/fHnPd1q1b6dOnD926deOjjz7i7bffbvZ+REREOoLdVdX8473V/PmVpXHb9OmWw3XHj6S0rJLSXRWU7qrc83rF5p2U7qpkW3klDVVJ9uzaKUxwO5PXLYfeuTm8/NGGmD27//PPeXz3H3OJyEvp2aUTRf26c2hhb75wWAHD87sxol93hud3p1+PzgkPU5mpNaatTUlrlFScGPn5+UycOJFDDjmE3NxcBg4cuGfdKaecwp133slhhx3GqFGjmDBhQou/g4iISHtUXlnNo2Gyum5bOYcP78OZYwq4/80VlFXuLePLzcnmx184uNG/3dU1zvbyyjoJ7daySj7dWRG+r2RrbdJbVknxp2XsrKiOu61vnTCSovzg5qcR/brTp1tO0sZPP3tsYYdLUqN1uBuxOoKO9n1FRKR9K6+s5m/vrOLOV5eyYftujijqy7dPHMnR+wazXbbm6AENlRG+ccPxKdlnR6MbsURERKRN2VVRFSary9i0YzcT9unL7ZPHctS+dW9Wbs1eyFSUEUpilLSKiIhIRtm5u4qH3l7JtNeWsXlnBRP3y+dPx4/lyH3yG/9wiqm+NH2UtIqIiEhG2LG7igfeWsFd/13Olp0VfG5kP759wkjGFfVNd2h1qL40PZS0ioiISFptK6/kgTdXcNfryyndVcmxo/pz3fEjOXx4n3SHJhlESauIiIikxdaySu57YwV3v76MbeVVnHDAAK47YSRjhualOzTJQEpaRUREpFWV7qrgnjdWcO8by9leXsVJBw3kW8eP5NAhvdMdmmQwJa0ZqEePHuzYsSPdYYiIiCTVpzsruPv15dz35gp27K7ilIMHcd0J+3FwgZJVaZyS1ni2r4PHroDz7oOeAxttLiIiIsQcM/VzI/tx1+vLeeDNFeyqrOa0QwZz7fH7ceDgXukOV9oQJa3xvPorWPU2vPpLOOM3LdrU97//fYYPH84111wDwM0334yZ8dprr/Hpp59SWVnJz372M84666xkRC4iIpIWM+YU1xnDtLi0jP/95zzMoKrGOeOwAq47fj/2H9gzzZFKW9TxZsT61w2wbn78Dax6g5gTEZvBsImxPzPoUDj1tribnDNnDt/5znd49dVXATjooIP497//TV5eHr169WLTpk1MmDCBjz/+GDNrcXmAZsQSEZF0iDdbVG5ONk9fN5H9BihZlcZpRqxEFYyHT5dD2WbwGrAs6JYPfUY0e5Njx45lw4YNlJSUsHHjRvr06cPgwYO5/vrree2118jKyqK4uJj169czaNCgJH4ZERGR1uHulMRIWCGYhlUJq7RUx0taG+gR3ePp6+H9+6BTV6iugAPPbHGJwHnnncdjjz3GunXrmDx5Mg8//DAbN25k9uzZ5OTkUFRURHl5eYv2ISIi0trKKqp5cm4x97+1knjXbgvycls1JmmfOl7SmoidG+DwK2DcFTDrXtixvsWbnDx5Ml/72tfYtGkTr776Kv/4xz8YMGAAOTk5vPzyy6xcuTIJgYuIiLSOVZt38dA7K3n0vdVsLavkgEE9+fK4ITw5r4Tyypo97XJzspkyaVQaI5X2QklrLJMf3vu6hT2stQ4++GC2b99OYWEhgwcP5qKLLuILX/gC48aNY8yYMRxwwAFJ2Y+IiEiq1NQ4r3+yiQfeWsF/PtpAlhmnHDyIy44uYnxRH8yMo/btV2/0AE15KsmgpLUVzZ+/9wawfv368dZbb8VspzFaRUQkk2wvr+Tx2Wt44O2VLNu4k349OnPdcftx4ZHDGdS7a522Z48tVJIqKaGkVURERGL6ZMN2HnhrJY/PXsPOimrGDM3jd18ew6mHDqJLp+x0hycdjJJWERER2aO6xvnPovU88NZKXv9kE52zszhj9GAuO6qI0UPz0h2edGBKWkVERIRPd1bw6KzVPPjWSopLyxjcuytTJo1i8vih5Pfoku7wRDpO0urumFm6w0i59jZZhIiIpNaHJVu5/80VPDm3hN1VNUzYpy8/OuNATjxwIJ2ys9IdnsgeaU1azewU4HYgG7jL3W+LWt8beAgYRhDr/3P3e5u6n65du7J582by8/PbdeLq7mzevJmuXbs23lhERFrdjDnFrX5nfax9nn7YYP69YB33v7mCWSs/JTcnmy8ePoTLjipi1CBNAiCZKW3TuJpZNrAEOAlYA7wHXODuCyPa/ADo7e7fN7P+wGJgkLtXxNturGlcKysrWbNmTYcYvL9r164MGTKEnJycdIciIiIRZswp5sbp8ymrrN6zLDcnm1vPPTRliWusfXbKMnJzsti+u5rh+d24ZMJwvjRuKL1z9XdDMkMmTuN6BPCJuy8DMLO/A2cBCyPaONDTgu7RHsAWoKqpO8rJyWHEiOZPwyoiItJSU2curpM8ApRVVnPLMwvpl6Ka0VueWVhvn1U1TkW1c+/l4zlm//5kZbXfK5DSvqQzaS0EVke8XwMcGdXmj8BTQAnQE/iyu9dEtcHMrgKuAhg2bFhKghUREWmu8spqikvLYq7bvLOCi+9+p1Xjqaiq4bgDBrTqPkVaKp1Ja6z/2kXXKkwC5gLHA/sCL5jZf919W50PuU8DpkFQHpD8UEVERJqmusZ5c+kmnpxbwswF6+K269ejM3+++PCUxPCNh2azaUf9irqCvNyU7E8kldKZtK4Bhka8H0LQoxrpCuA2DwpvPzGz5cABwLutE6KIiEji3J15a7by5Nxinp63lk07dtOzSydOOWQQ/Xt24d43llNWufeCYW5ONjedfhDji/qmJJ6bTj8oZh3tlEmjUrI/kVRKZ9L6HjDSzEYAxcBk4MKoNquAE4D/mtlAYBSwrFWjFBERacTSjTt4cm4JT80tZsXmXXTOzuL4AwZw9tgCjh01gK45wexR+w/s2aqjB9Ruu7VHLBBJhbSNHgBgZqcBvyMY8uoed/+5mV0N4O53mlkBcB8wmKCc4DZ3f6ihbcYaPUBERCTZ1m0t55kPSnhybgnzi7diBkfvm89ZowuZdMgg3Y0v0kzxRg9Ia9KaCkpaRUQkVbaWVfLvBWt5cm4Jby3bjDscNqQ3Z40p5IzDBjOwl8bJFmmpTBzySkREJOOVV1bz0kcbeHJuMS9/tJGK6hpG9OvOt44fyVljCtinf490hyjSIShpFRERiRJ95//23VX079mFiycM5+yxBRxa2Ltdz7AokomUtIqISIdVd4rTrpw/fiiluyrr3fl/9thCJuyTT7YG4hdJGyWtIiLSIQVTnH6wZwiq4tJyfvvCx2QbnHTQIM4aU8BxB+y9819E0ktJq4iIdBjrtpYzd/WnzF29lXveWE5FVb1JFhnQqyt3XpKawf5FpPmUtIqISLu0vbySD9ZsZe7qUuatLmXemlLWb9sNQE62UVkde/ScdVvLWzNMEUmQklYREWnzKqpqWLxu+55e1HlrSlm6cQe1ozqO6Nedo/ftx+ghvRk9NI8DB/fihF+/SnFpWb1taYpTkcykpFVERNoUd2fl5l3MW1PKnFVBD+qHJdv2XOrv16Mzo4fkcdboAkYPzeOwIb3J69a53namTBqlKU5F2hAlrSIikhHq3sm/d7rRTTt288GaUuauKmXumq3MW13K1rJKIEgyDy3szeVHFzF6SB6jh/amMC83oeGoNMWpSNuiGbFERCTtou/kB8g26JWbw6e7ggQ1y2D/gT0ZMzSPMUPzGD00j5EDetApOytdYYtICmhGLBERSTt3Z/POClZu3smKTbuC5827+NeCtfVujKr2YDaqH552IKOH5nFIYS+6ddafLZGOqkk//Wb2V+Aud38nRfGIiEgb5+5s3L6bFZt3sWLzzj2J6YpNO1m5eRc7dlftaZtlMKRPt7h38pdX1vC1z+/TWqGLSAZr6n9ZrwC+YmaLgLuAB919c/LDEhGRdItXYwpQU+Os315ep7d05eadLN+0k1VbdrGrYu/NTZ2yjKF9uzE8vxvji/oyPL8bRfndGZ7fjSF9utG5UxYTb3tJd/KLSIOaVNNqZoOAywmS15HAbuBJ4G53fyEVATaValpFRFouqDGte2d9pyzjgEE9qaiuYeXmXeyOGJi/c3YWQ/vmhslod4r6dQue87tRkJdLTiN1p7H2l5uTza3nHqobo0Q6mHg1rc2+EcvMPg9cCXwRyAVWAfcA97n76hbE2iJKWkVEWsbdGf/zF9m0o6Leuk5ZxnEHDKAovzYpDXpMC/Jyyc5q/I79hjTUsysiHUfSk9aIDfcELiBIYMcBNcALwF+Ap7yVhydQ0ioi0jzlldU8Na+E+99cwYcl22K2MWD5bae3bmAi0qGkcvSAXKBX+DBgJ3AkMAn40My+7O6LkrAfERFJgTWf7uKht1fx6Hur+HRXJfsP7EHv3Jw9Y6FGUo2piKRLs5JWM8sCTiXoXT0dyAHeBb4K/B2oAi4Gfgn8FfhsMoIVEZHkcHfeXLqZ+99cwYuL1gNw8kGDuPTo4Ry1Tz5Pzi3RbFEiklGaOuTVvsBXgMuAwcA2YBowzd3nRzW/x8y6Af8vGYGKiEjL7dhdxRPvr+H+t1byyYYd9O3emauP2ZeLJgynMKIXVbNFiUimaWpP68fh81vAD4FH3b28gfYrgLXNiEtERJJo2cYdPPDWSh6fvYbtu6s4tLA3/+9LoznjsMF0zcmO+ZmzxxYqSRWRjNHUpPUPwF/cfWEijd39GeCZJkclIiItVl3jvLJ4A/e/tZLXlmwkJ9s4/dDBXHp0EWOH5mHWsrv9RURaU5OSVnf/dqoCERGR5Ni6q5J/zFrNg2+vZNWWXQzs1YXvnrQ/k48YyoCeXdMdnohIszS1pvV84Ax3vzTO+vuBp939sWQEJyIiiVu0dhsPvLWCJ+YUU15Zw/iiPnzvlFFMOnhQo4P7i4hkuqaWB1wHLG1gfXXYRkmriEgrqKyu4fkP13P/Wyt4d/kWunTK4uwxhVx69HAOLuid7vBERJKmqUnrgTSckM4BvtD8cEREJJbo2aKuPnYfSndW8vA7q1i3rZwhfXL5wWkHcP64oeR165zucEVEkq6pSWt3gt7UeBzo2fxwREQk2ow5xXXGTC0uLeNHMz4E4HMj+/Gzsw/huAMGtHgaVRGRTNbUpHU5wUQBf4yz/rPAqhZFJCIidUydubjOIP+1BvTswoNXHpmGiEREWl9TK/OfAL5kZldGrzCzrwBfAqYnIzAREQmUlJbFXL5x++5WjkREJH2a2tN6G3AWMM3MrgfmEpQEjAEOAhYDv0hifCIiHdrqLbvIzjKqarzeuoKIGaxERNq7JvW0uvt2YCLwF4JpXC8ELgIKgD8DR7v7tmQHKSLSEb2/6lPOueMNsrOgc9SQVbk52UyZNCpNkYmItL4mD9zn7lvd/RqgHzAQGAT0c/dr3b00yfGJiHRIT80rYfK0t+nWuRPPfuvz/Oq8wyjMy8WAwrxcbj33UE2xKiIdSlPLA/Zwdwc2tmTnZnYKcDuQDdzl7rfFaHMs8DsgB9jk7se0ZJ8iIpnM3fn9fz7hty8uYXxRH/5yyTj6du/MfgN6KEkVkQ6tWUmrmWUDBwB9iNFb6+6vJbiNPwEnAWuA98zsKXdfGNEmD7gDOMXdV5nZgObEKyLSFpRXVnPD4x8wY24J544t5NYvHkqXTtnpDktEJCM0OWk1s+8DNwC9GmiWyG/ZI4BP3H1ZuN2/E9zktTCizYXAdHdfBeDuG5oar4hIW7B5x26uenA2s1d+ypRJo7jm2H0x07irIiK1mlTTamZfBW4lGDXgJsAILt1PBbYAs4CvJLi5QmB1xPs14bJI+wN9zOwVM5ttZpc2JV4Rkbbg4/XbOfuON1hQvJU/XfgZvnncfkpYRUSiNLWn9WrgbXc/zszygZ8Dz7r7S2Z2O0Eym+i1rFi/kaPHdOkEHA6cAOQCb5nZ2+6+pM6GzK4CrgIYNmxYot9FRCTt/vvxRq556H265GTz6NePYszQvHSHJCKSkZo6esCBwD/D17UJZicAd18LTAO+neC21gBDI94PAUpitPm3u+90903Aa8Do6A25+zR3H+fu4/r375/g7kVE0uuht1dy+b3vUdgnlyevnaiEVUSkAU1NWquBneHr2ue+EetXACMT3NZ7wEgzG2FmnYHJwFNRbZ4EPmdmncysG3AksKiJMYuIZJTqGuenTy/kphkLOGb//jz2jaMp1EQBIiINamp5wCpgBIC77zaz1cDngL+H68cT1LY2yt2rzOxaYCZBScE97v6hmV0drr/T3ReZ2b+BD4AagmGxFjQxZhGRjLFjdxXfemQOL320gSsmFnHT6QeRnaX6VRGRxjQ1aX0NOB24MXz/T+A7ZpZL0Gt7MXBPohtz9+eA56KW3Rn1firBjV4iIm1acWkZV973Hh9v2MEtZx/CJROGpzskEZE2o6lJ6+3APDPLdfcy4McEd/hfFq5/nmA4LBERiTB3dSlfvX8Wuyuruefy8Ryzv+rvRUSaoklJq7svBhZHvN8JnGlmvYFqd9+R5PhERNq85+av5fpH59K/Zxf+9rUj2X9gz3SHJCLS5iSctJpZD+D3wL/c/Z+R69x9a7IDExFJ1Iw5xUyduZiS0jIK8nKZMmlURkx56u7c8cpSps5czOHD+/CXSw6nX48u6Q5LRKRNSjhpdfcdZjYZeCOF8YiINMmMOcXcOH0+ZZXVQFA3euP0+QBpTVx3V1Xzg+kLePz9NZw1poBffvEwuuZoSlYRkeZqak3rQqAoBXGIiDTL1JmL9ySstcoqq7n5qQ85qKAX+/bv0ep352/ZWcHVD87m3RVbuP7E/fnWCZrhSkSkpZqatP4KuMPMHoyelUpEJB2KS8tiLi8tq+Tk375G987ZHDqkN6OH5jF2aB6jh+YxqFfXlCWRn2zYwZX3v8fareX8/oKxnDm6ICX7ERHpaJqatB4ArAbmm9kzwMfArqg27u63JCM4EZF4Nu/Yzc+ejT/XyICeXbjh1AOYu7qUeatLuef15VRW+551o4fmMSZ8HDqkN7265rQ4pjc+2cQ3HppNTnYWj3xtAocP79PibYqISMDcvfFWtY3NahJo5u6etsKtcePG+axZs9K1exFJMXdn+vvF/OzZhezYXcXxowbw6scbKa/c++spNyebW889tE5N6+6qahat3c7cVZ8yb81W5q0uZdmmnXvW79u/e53e2AMG9aJzp8QnDXzk3VX8aMYC9unfnbsvG8/Qvt2S84VFRDoYM5vt7uOilze1p3VEkuIREWmylZt38sMnFvD6J5s4fHgfbj33UPYf2DOh0QO6dMre07Naa+uuSj4oLmXuqlLmrSnltSUbmf5+MQCds7M4qKDXns+MHppHUX63PWUFkfvs3iWbHbur+fz+/fnjhWOT0msrIiJ1NamntS1QT6tI+1NVXcNdry/ndy8uoVNWFt8/9QAuOmIYWUm+wcrdKdlavieJnbu6lPlrtu650at3bg6jh+aR28l4efEmKqr39u5mZxlTv3go5x4+NKkxiYh0NMnqaRURaVUfrCnlhsfns3DtNk4+aCA/PesQBvXumpJ9mRmFebkU5uVy+mGDgSBh/njDDuatrk1kt7Jo7bZ6n62ucX79wsdKWkVEUqRJSauZ3ZNAM3f3K5sZj4gIADt3V/GbF5Zw7xvL6dejC3de/BlOOWRwq8fRKTuLAwf34sDBvZh8xDAARtzwLLGuUZXEGclARERarqk9rZcn0MYBJa0i0mwvL97ATU8soLi0jIuOHMb3Tz0go+pEC/JyYw61VZCXm4ZoREQ6hsRvjQXcPSv6AeQAo4C/Am8DGuNFRJpl047dfOuROVxx73vkds7mn1cfxc/POTSjElaAKZNGkRs1u1VuTjZTJo1KU0QiIu1fi2ta3b2aYLzWr5vZ08AvgW+0dLsi0nG4O/+cvYafP7uIsopqvnPiSL5x7L506ZSZ057WjkzQ2IgFIiKSPMm+EetfwM0oaRWRBK3YtJMfPDGfN5duZnxRMIzVfgN6pjusRp09tlBJqohIK0p20poP9EjyNkWkHaqsruGv/13G7S9+TOfsLH5+ziFcMD75w1iJiEj7kJSk1czygBOB64HZydimiLRfc1eXcsPjH/DRuu2ccvAgfnLWwQzslZphrEREpH1o6pBXNRBzpBcAA7YA321pUCLSPu3YXcWvn1/MfW+uYGDPrvzlksOZdPCgdIclIiJtQFN7Wh+gftLqBMnqEuARd9+ejMBEpH156aP13PTEAtZuK+eSCcOZMmkUPTNsVAAREclcTUpa3f3yFMUhIu3IjDnFe+6sH9irK4N6d2Hu6q3sP7AHj114FIcP75vuEEVEpI3RNK4iklQz5hRz4/T5lFVWA7BuWznrtpVz2iGD+N3ksXTu1KThoUVERIAmTi5gZt80sxcbWP+8mX295WGJSFtUWV3Dz55duCdhjTRvzVYlrCIi0mzNmcZ1VgPrlwBfAf7S3IBEpG0pLi3j1cUbeXXJBt74ZDM7dlfFbFcSY9pTERGRRDU1aR0J3NvA+g+BC5sfjohkuvLKat5bsSVMVDfy8YYdABT07soXRhcw88N1bNlZUe9zBXm5rR2qiIi0I01NWnOAhgZT7NrIehFpg1Zu3skrYZL61tLNlFVW0zk7iyP36cuXxw/lmP37s9+AHpgZR47oW6emFSA3J5spk0al8RuIiEhb19SkdQlwEvCbOOtPBpa2KCIRSbuyimreWrZpT2/qis27ACjK78b544ZwzKj+TNgnn26d6/8KqZ3atHb0gIK8XKZMGqUpT0VEpEWamrQ+AtxqZrcAt7h7BYCZ5QA3ESStNyU3RBFJNXdn6cYde3pT31m+hYqqGrrmZHH0vv24YuIIjtm/P0X9uie0vbPHFipJFRGRpGpq0vpb4FTgh8A3zOwjgskFDgT6Av8Ffp3UCEWkxSLHTa3t+TzhwAG8uXQzryzeyGtLNlIc3ig1ckAPLp0wnGNG9Wd8UV+65mSnOXoREREw93izssb5QNCrej3BDVcjw8VLgIeB2929MqkRNtG4ceN81qyGBjgQ6Viix00FyDJwD/7H2aNLJybul88x+w/g8/v3Y0ifbukLVkREOjwzm+3u46KXN3lygTAp/VX4EJEMN3Xm4nrjptZ4kKzeddk4PjOsj8ZPFRGRjJfWv1RmdoqZLTazT8zshgbajTezajM7rzXjE2nrqmt8z2X/aDt3VzFhn3wlrCIi0iY0dUasn5jZggbWf2BmCd2IZWbZwJ8IamQPAi4ws4PitPslMLMpsYp0dFt2VnD5ve/GXa9xU0VEpC1pahfLOcALDax/AUi0N/QI4BN3XxaOQvB34KwY7a4DHgc2NCVQkY5s7upSzvj9f3ln+Ra+PH4IuVE3U2ncVBERaWuamrSOAD5qYP3isE0iCoHVEe/XhMv2MLNCgkT5zibEKNJhuTsPvr2SL935JllZxuNXH80vvziaW889lMK8XAwozMvl1nMP1ZBUIiLSpjT5Riwgr4F1fYBEx8exGMuihzL4HfB9d682i9U83JDZVcBVAMOGDUtw9yLtS1lFNT98Yj7T5xRz7Kj+/O7LY8jr1hnQuKkiItL2NTVp/ZDgEv4vo1dYkFWeScM9sZHWAEMj3g8BSqLajAP+Hias/YDTzKzK3WdENnL3acA0CIa8SnD/Iu3Gik07ufqh2Sxev53rT9yf647fj6ys+P/RExERaWuaWh5wNzDBzO4zs/61C8PX9wATwjaJeA8YaWYjzKwzMBl4KrKBu49w9yJ3LwIeA66JTlhFOrrnP1zHF/7wOuu2lXPv5eP59okjlbCKiEi706SeVnf/q5kdA1wKXGJmawku6RcQXO5/1N3/nOC2qszsWoJRAbKBe9z9QzO7OlyvOlaRBlRV1/DrF5bw51eWcmhhb+646DMM7auJAUREpH1q8oxYAGZ2PnARsB9BsroYeNjdH0tueE2nGbGkI9i0YzffemQOby7dzAVHDOPHXzhI062KiEi7kLQZsQDc/R/AP1oclYg02furPuWah97n010V/Oq8wzh/3NDGPyQiItLGNStpNbNxwJEEowVE18W6u9/S0sBEpC5354G3VvKzZxcyqHdXHv/G0RxS2DvdYYmIiLSKJiWtZpYLTAdOJigLcPYOXeURy5S0iiTRrooqfjB9PjPmlnD8AQP47flj6N0tJ91hiYiItJqm9rT+H0HC+nPgP8DLwGUEs1XdCOQS3KQlIkmybOMOvvHQ+yzZsJ3/OWl/vnmchrMSEZGOp6lDXp0H/NPd/w9YEC4rdveZwIlAZ+Dy5IUn0rH9e8E6zvzjG2zYXs79VxzBdSdoOCsREemYmpq0DgVeDV9Xh8+dIRjCCniEYLxVEWmBquoabv3XIq5+aDb79u/OM9/6HJ/fv3/jHxQREWmnmloesD3iM9uBGoIxWmttBQYlIS6RDmvj9t1c98j7vL1sCxcdOYz/+8JBdOmk4axERKRja2rSuhTYH8Ddq83sQ4KSgXvCaVzPBVYnN0SRjmP2yi1c8/D7lO6q5NdfGs0XDx+S7pBEREQyQlPLA14Evmhmtd0+fwFOMbOlwMcEda2JTuMqIiF35943lvPlv7xN15xsnrhmohJWERGRCE3tab0NeJBwmCt3v8PMugIXE9S4/hX4VVIjFGnndu6u4obp83l6XgknHjiAX58/ht65Gs5KREQkUpOSVnffQTBla+Sy3wC/SWZQIu3ZjDnFTJ25mJLSMvr37IIBG3fsZsqkUXzjmH01OoCIiEgMzZoRS0SaZ8acYm6cPp+yymDwjQ3bdwPwjWP25ZvH7ZfO0ERERDJaU2taRaSZdu6u4mfPLtyTsEZ6al5JGiISERFpO9TTKpICVdU1LFm/g3lrSpm3upS5q0tZsn47NR67fUlpWesGKCIi0sYoaZUGRdZfFuTlMmXSKM4eW5jusDKKu1NcWsbc1UGCOm/1VuYXb93To9o7N4fRQ/M4+aCBPPzOKjbvrKi3jYK83NYOW0REpE1R0ipxRddfFpeWceP0+QAdOnHduquSeWtK9yapa0rZtCNIRDt3yuLggl58efxQxg7LY/SQPIbndyMYxhj26d+jzjEFyM3JZsqkUWn5LiIiIm2FklaJa+rMxfXqL8sqq5k6c3G7Slob6k0ur6xm0dptEQnqVpZv2gmAGezbvwfH7D+AMcPyGDMkj1GDetK5U/xS8drtqvdaRESkaZS0Slzx6izbU/1lrN7kKY/N45+zVrN9dxWL1m6jsjooRB3QswtjhuZx3uFDGDs0j0OG9KZX16aPp3r22EIlqSIiIk2kpFViqqquoUunLMqrauqtc+Ciu97m0qOKOPHAgWS34XFFf/nvj+r1JldWO28u3cyEffK58rP7MGZoHmOG5jGod9c0RSkiIiJKWqUed+fHT31IeVUNOdm2p6cRoGtOFiccMIA5q0r5+oOzKczL5eIJw5k8fih9undOY9SJK6uo5sVF63lybjFrt5bHbffIVRNaMSoRERFpiJJWqeeOV5by8Dur+Pox+3DgoF4x6y+rqmt4cdF67n9zJb/890f87sUlnDm6gMuOLuKQwt7p/gr1VFXX8Ponm3hqbgkzP1zHzopqBvXqSo8undixu6pee93NLyIiklmUtEodj89ew9SZizlrTAHfn3QAWVkWs/6yU3YWpxwymFMOGcziddt54K0VTH+/mH/OXsPhw/tw6VHDOfWQwQ3elJRq7s77q0p5cm4xz36wls07K+jVtRNnjingzNGFHDGiL0/PK9Hd/CIiIm2AuccZ7byNGjdunM+aNSvdYbRJ//14I1fc+x7ji/py31fG06VTdpM+v7Wsksdmr+HBt1awYvMu+vfswoVHDOOiI4cxoFfr1YN+vH47T84t4cl5xazeUkaXTlmceOBAzhpTwDGj+tf7XhqLVkREJHOY2Wx3H1dvuZJWAfiwZCtf/svbDOmTyz+uPqpZd8XXqqlxXv14Iw+8uYKXF2+kU5Zx6qGDueyo4Rw+vM+eMUuTqaS0jKfnlTBjbgmL1m4jy2Difv04e0whJx88kJ4t+D4iIiLSepS0SlxrPt3FuXe8SXaWMf2aoxncO3n1nCs27eTBt1fyj1mr2V5excEFvbjsqCLOHFNA15ym9eRGK91VwXPz1/Hk3GLeXbEFdxgzNI+zxhRwxmEF9O/ZJUnfQkREJMr2dfDYFXDefdBzYLqjaVeUtEpMpbsqOO/Ot1i/rZzHrj6aUYN6pmQ/O3dXMWNuMfe/uYIl63eQ1y2HL48fysVHDmdo324Jb2fvnf8lvLpkA5XVzr79u3P2mELOHFPA8PzuKYlfREQyXGsnkc98F2bfC4dfAWf8JvX760CUtEo95ZXVXHr3u8xdXcr9XzmCo/bNT/k+3Z23l23h/jdX8PzCdQCccOBALjuqiIn75fPk3JJ69aVnHDa43p3/A3t14czRBZw1ppCDC3qlpORARETakCevhTkPwejJcML/gTt4Td0H1F8W3Q6P8dmI9w9/Eaor6+8/uwvctD6YLjFVOkjvrpJWqaOmxrnukTk8O38tv79gLGeOLmj1GIpLy/jbOyt55N3VbNlZwYCenfl0V2WdcWGzs4yunbLYWVFNr66dOO3QwZw1Jrjzvy1PaiAiGaS1E4EOknikTE0NfLoc1s2H9Qvgv7/em5CmW1YnyO0T59EXcvNir+vSC7ISGG2ng/TuKmmVOm55ZiF3v76cH5x2AFd9ft+0xlJeWc2zH6zlhukf1ElYa+XmZHH75LEx7/wXEWmx1k4EOkjikRS7t8P6hbB+PqxbECSp6xdC5c5gvWVBnxFQXQHb10JNFWTlwODD4KBzgiTRsoLeT8uKeBgQvSy6Xfg6Vrs3/wCLn4PsTlBdBUUTYeQkKPs0xqM0eK7YHv97WhZ0zYud0HbrC69NDb5btE5d4KYNST/se6TpP1hKWmWPu/67jJ89u4jLjy7ix184KGMurY+44VlinY0GLL/t9NYOR0Tau1sGQPXu+sstCw78QvL3t+jp2D2CWTlw5fPQewh075/ay8uZyh1KVwVJ6boFe5PUT5fvbdOlNww6BAYesvd5wIGQkwtPXw/v3wfZnYMENtX/Ifj7RdBjIIy7AmbdCzvWw+SHG/5MVQWUl8ZJbBt4lG9teLtdezfQsxuv1zcPshMYVSdN/8GKl7RqcoEO5pkPSvjZs4s45eBB/OiMzElYIZiFqri0LOZyEZEWqyyDlW/Cspdh2SsxElaDLj2hxwDYuDj5+++zD+xcD7t3QOR/0Wsq4a/HBa+zO0OvAug1BHoXQq/C8DnifW6fpie2mVQCUVkGGxbu7TldtwDWfwi7a5Mzg74jYNChMObCvUlq76Hxv/fODUFiFZlEplJkgppoMtepc3Bu9RjQtH1VVwWJ67+mwILpQQlCTRUMPRL2ObZ+kvvpyjDZLW24bKJzz70JbHRS++bv6/bszro7eKS6Z7cRaU1azewU4HYgG7jL3W+LWn8R8P3w7Q7gG+4+r3WjbD/eWbaZ7z46j3HD+/C7yWMyriZ0yqRRmp1KRJKnpgbWzYOlLweJ6qp3gkQ1KweGTYDjb4KSebD42b09dId+KbU9Snt6BLsE+ztsMhx5FWwthm3FsHVN+FwMK9+C7SX1LwvndAsT28KgdzZWYtu1V93PvPorWPU2vPrL1ukxq93fC/8Hh3yx7uX9zZ/sTaZyusPAg+HQL4bJ6aEw4CDo0qNp+2tOEtlWZHeC7vlQtRvGfaVuYn7cjfE/V1MT/EcgVqlCrMeGhXtfR59znXLhwDPg5J+n9Ks2Jm3lAWaWDSwBTgLWAO8BF7j7wog2RwOL3P1TMzsVuNndj2xouyoPiG3J+u2c9+c36d+zC49/42jyunVOd0gxaXYqEWmRT1cEvahLX4blrwZ/gAEGHBz0Su17HAw/GjqHw+M15zJvSzR1fzXVsGND/YR225q9ie72dRBdXNWlV5C8blocpyQhG47+Nnh1sI+aqojnquAzcZfVLq8OPx+xbN38+rHU6j2s/uX9PiMSuwFJWo87VOyAZ66H+Y8FZQQ1Va1aIpBxNa1mdhRBEjopfH8jgLvfGqd9H2CBuzeYwShprW/9tnLO+dMbVNY4079xdJPGRRWRDKK7zusr+xSW/zfoSV368t4ayJ6DYZ/jgiR1xDHt+3hVVwY3IW0riUpsi2HL8qBnM1btblanvQ/LDhLZrOyIZVkRbcJ1ll1/2Z7PdwpKHTYsCs5Vrw56tYdPhNOmQv/9W//YSPO19n/oImRiTWshsDri/RqgoV7UK4F/pTSidmh7eSWX3fMuW8sqefTrRylhFWnLWvsSL2Reoly1G1a/G/SmLnsZSuYEPYCde0DRZ+HIq4NEtd/+HeeGpuwcyBsWPGLZU5KQEyS4n7kcvvDb1MVTu79OXYMSiPx9lbC2RRlYcpHOpDXWb5OY3b5mdhxB0vrZOOuvAq4CGDYszg9tB1RRVcM3HnqfTzbs4J7Lx3NIYe90hySSOpmWXCVT9F3utTdFZHWCU3+5dxieBofwiVoXt23U8rfuCGor/30DnPjjoAaxc7egxi0Vl3Wj/x3dg1q72rrUlW9C5a6gZ6/wcPj8lKBHdci4xO6G7oha+yal1t6fdBgZXx5gZocBTwCnuvuSxrbbmuUBmVx/6e78zz/mMX1OMVPPO4wvjRua7pBEUqutj325eztsWRb1WB48b1+b7uhiy+kWDDdUm8jmdAtqRWuX177u3C1ok5O793Wd54htvHwrzH0Yij4HPQcFPao7w7uV80furUst+mww1I+ItDuZWB7wHjDSzEYAxcBk4MLIBmY2DJgOXJJIwtqaZswprnOne3FpGTdOnw+QEYnr1JmLmT6nmP85aX8lrJIeLe35rK6C3dvCcQ1LgyFf9jwi3s+6p+6NJrW9kJYNx94A3fKDsS+794fu/YJH17zkXDpu6ncsK62fkNY+ahOzWj0GQt99YN8TguF/VrweJHC1d7mPuQhO+knD01HWLiPW8sj2Maax3LkJ3rsLVr0V7C+7MwweDaNODY5t5S6o2Bk8V5btfV2xC3asC54ry4JB4Ct2xa6pbMiK14Jny4Iz/xgkq3n6XSbSkaUtaXX3KjO7FphJMOTVPe7+oZldHa6/E/g/IB+4IxxPtCpW5p0OU2curjM0E0BZZTVTZy5Oe9L64NsrueOVpVxwxFCuPX6/tMYiHdgrvwwuKz9/E0z8VsOJZ/nWqPWlwd2rDbHsoKetV2HQtnxrkHBZVtC7Z1nwcpzhWbI6Qbd+dRPZ2td7lvcPhpnp3j+ol4yV5MaqMd21JUaP6TLYvBTKttT9fM+CIDHdf1JQ99d3n+DRZ0T9IX9K5tQf7qZ7v8b/HVpi6cuw4r97axMHHQaf+5/mbau6Kkxwd+1NbiMT321rYd7fYO0Hwc082V3goDODIXbaW7mHiDSLZsRqpnizNwF8dMspdM1Jz3Sjz3+4jqsfms1xowbwl0sOp1O2hhLJSOmov2zuPmuqw6Qywdlb1swi7pA30br0Cno9u/YOZ3WJeN21d8PrIhPJeLPhVFfCrs2wc2P42BQ+wvfR6+Ilyp26hslsmNAu/U+Cc51bMI5m3xF7E9K+YXLapyi4HJ7JWvvu4dae1UhEMlImlge0afFmbwIY//MXOe2QwZw1poAj98lvtUH831/1Kd/6+xwOLezNHy4cq4Q1k734k6AX8rn/DXquYs573dgc2U2YJ9uy4JXbgn3O/AEc+fW6ieauLY1MIdhAEtqlV92ZVPY/JRgbsnRVMLZfdg4Ujg/22adobwLapVcwXE4yxLvxIzsnqIvsOSix7VSW7U1q6yS0G2FnxPseg8KxMWsTVwuS2f1OCMae3NNjWgQ5XZPzHdOhte8e1g08ItIA9bQ2U3RNK0DXnCwuP7qIDdt3M3PBOnZWVDOwVxe+cFgBZ40p5JDCXimbNnX5pp188c9v0rNrJx7/xtH069ElJfuRZqqphuL34d5JweuMY0Ey2a2huapjPLr2jn3HdkfoMXv6OxHfsbJ9fkcRkTRQT2uS1datxhs9oPycal5ctJ4n55Zw/1sruOv15ezTvztnjS7kzDEFjOjXPWmxbNy+m8vueReA+684Qglrpti1BT75D3z8PHzyYljPaMFl5vLSsBeyMwwZD6MvDKdd9Dg31cS5kQaPf/NN7evyrfDxC0HvZ+0+hx4ZzIaTv8/e5DNZvZ7QMXrMdm6Ew7/Svr+jiEgGUU9rK9i6q5J/LVjLjLnFvLN8C+4wekhvzhpTyBmjBzOgZ/MvH+6qqGLytLdZsn47j3xtAmOH9Uli5NIk7rDugyBJXfI8FM8KksZu/WDkScFjn+PgP7e0fi9kR+j5FBGRdkE9rWnUu1sOk48YxuQjhrF2axlPzyvhybkl/PSZhfzs2YVM3K8fZ44uYNIhg+jVNfHBsauqa/jmw++zoHgr0y4Zl7qEtT0P2t5S5VuDYYg+fh4+fjEY6geg4DPw+e/ByJOhYGzdQdjT0QvZEXo+RUSkXVNPaxp9smE7T84NEthVW3bRuVMWJx44gDNHF3LcAf3p0in+5Vp35wdPzOeRd1fz83MO4aIjh6cu0LY+aHsyucPGj8Ik9YVgDMuaquDy+r4nBEnqfidAjwHpjlRERKRNitfTqqQ1A7g7c1eX8uTcEp75oIRNOyro2bVTvREIImfg6tG1E9vLq/jmcfsyZdIBqQkseurIWtk5cNVrwd3S3fomtxYyE1XshOX/hY9nBonq1tXB8oGHhJf9T4YhR0C2LlyIiIi0lJLWNqKquoY3lm7mybnFdUYgOHBQT95atoXdVXvHhsw24/996TDO+cyQ5Ox8+7pg1p2Vb8CKN4IbdxplQeLavX/dcSzrDNgesa5rXuPzlWfCGKablwYJ6sfPB8ekencwzeS+xwWJ6n4nQe/0z3wmIiLS3ihpTZUUJlhlFdX856NgBIIXFsauQSzMy+WNG45v3g62FocJapiobv4kWN65JwybAEUTYc1sWPxs0LtaVQEHnxPMyrMrcpD2qOddm4LxPWPJ6hQxrWa/2LMSzX0EPnoGDjkXjr2RYNzR6LFHY4xF2uR2EcOPPfVteP9+GHAQVJXDlqXB8vyRQU/qyJNg+NHQSSMziIiIpJKS1lRppXrPeDNwGbD8ttMT20jp6jBBfT3oSf10ebC8S28YfhQMnxgkqoNG773U3dwZcfbMRBQjoa03M9EmqNie2HdIKiPuoPlZneDa94IB4kVERKTVaPSAZPvZAKiKqPecdXfwyMqBK54L5kPvOShp9Z7xZuAqyMuN/QF3KF0ZJKe1iWrpqmBd17wgQT3ia8HzoEPjx9ncGXGaPBNROWxYBC/dEsx1Xl0RDM9U8Bk49Dzo0jP2eKR7xjWNfo7VJka73duCu/83fxLcUNUpFw48Q/Odi4iIZBglrc317Q/guSnBZezIOchrKuHuk4LXlg09B0OvgqD+sVdhMA95r8Lw/ZDgcnhjNZ7AlEmjuHH6fHpUbuKPnf/AtRXfYkdOPlMmjQoauMOWZXsv9694A7atCdZ1yw8ubU/4ZtCTOuDghPbZqnK6QuFYyBsONS8Hc71XV8DAg4PkOpWevh42LQn3uTuYXlQJq4iISEZR0tpcPQcF9ZiwN8EafSEcdU1QK7ptDWwr2ft67Qew+F9BvWSkrJwwqQ2T2cjXtYltt757Ztqqeea7jK9azI3dnqTXsd/ixOrn4fEwUd2+Nthm9/7hpf7vBM/9D8i8JDUejWEqIiIiMaimtSWaWu/pHkztuW1NmMwWw9Y14XNtors26K2N1Ck3THbj/Fv1GAhFnw0T1c9Cv/3r3mQkIiIi0kboRqy2oqYmuDkpOrHdvBRWvxPOX09QelAwBk7+BQw7UkmqiIiItAu6EautyMoK6il7DoTCw+uui54/fvAYGD4hHVGKiIiItColrW2Jai9FRESkg1LS2pY0d/gpERERkTaujdxSLiIiIiIdmZJWEREREcl4SlpFREREJOMpaRURERGRjKekVUREREQyXrubXMDMNgIrW3m3/YBNrbzPtkLHJj4dm/h0bOLTsWmYjk98Ojbx6djEl45jM9zd+0cvbHdJazqY2axYMzeIjk1DdGzi07GJT8emYTo+8enYxKdjE18mHRuVB4iIiIhIxlPSKiIiIiIZT0lrckxLdwAZTMcmPh2b+HRs4tOxaZiOT3w6NvHp2MSXMcdGNa0iIiIikvHU0yoiIiIiGU9Ja4LM7BQzW2xmn5jZDTHWm5n9Plz/gZl9Jh1xpoOZDTWzl81skZl9aGbfjtHmWDPbamZzw8f/pSPWdDCzFWY2P/zes2Ks75DnjpmNijgf5prZNjP7TlSbDnPemNk9ZrbBzBZELOtrZi+Y2cfhc584n23w91N7EOf4TDWzj8KfmyfMLC/OZxv8GWzr4hybm82sOOJn57Q4n23X506cY/NoxHFZYWZz43y2vZ83Mf92Z/TvHXfXo5EHkA0sBfYBOgPzgIOi2pwG/AswYALwTrrjbsXjMxj4TPi6J7AkxvE5Fngm3bGm6fisAPo1sL7DnjsRxyAbWEcwNl+HPG+AzwOfARZELPsVcEP4+gbgl3GOXYO/n9rDI87xORnoFL7+ZazjE65r8GewrT/iHJubgf9t5HPt/tyJdWyi1v8a+L8Oet7E/Nudyb931NOamCOAT9x9mbtXAH8HzopqcxbwgAfeBvLMbHBrB5oO7r7W3d8PX28HFgGF6Y2qTemw506EE4Cl7t7aE4NkDHd/DdgStfgs4P7w9f3A2TE+msjvpzYv1vFx9+fdvSp8+zYwpNUDywBxzp1EtPtzp6FjY2YGnA880qpBZYgG/nZn7O8dJa2JKQRWR7xfQ/2kLJE27Z6ZFQFjgXdirD7KzOaZ2b/M7ODWjSytHHjezGab2VUx1uvcgcnE/8PRUc8bgIHuvhaCPzDAgBhtdP4EvkJwxSKWxn4G26trw9KJe+Jc4u3o587ngPXu/nGc9R3mvIn6252xv3eUtCbGYiyLHnYhkTbtmpn1AB4HvuPu26JWv09w6Xc08AdgRiuHl04T3f0zwKnAN83s81HrO/S5Y2adgTOBf8ZY3ZHPm0R16PMHwMx+CFQBD8dp0tjPYHv0Z2BfYAywluAyeLSOfu5cQMO9rB3ivGnkb3fcj8VYlvJzR0lrYtYAQyPeDwFKmtGm3TKzHIKT/mF3nx693t23ufuO8PVzQI6Z9WvlMNPC3UvC5w3AEwSXVSJ16HOH4A/C++6+PnpFRz5vQutrS0XC5w0x2nTo88fMLgPOAC7ysNguWgI/g+2Ou69392p3rwH+Suzv3GHPHTPrBJwLPBqvTUc4b+L87c7Y3ztKWhPzHjDSzEaEvUKTgaei2jwFXBreCT4B2Frbvd7ehXVBdwOL3P03cdoMCtthZkcQnHubWy/K9DCz7mbWs/Y1wY0jC6KaddhzJxS3t6OjnjcRngIuC19fBjwZo00iv5/aJTM7Bfg+cKa774rTJpGfwXYnqi7+HGJ/5w577gAnAh+5+5pYKzvCedPA3+7M/b3TmneqteUHwR3eSwjulvthuOxq4OrwtQF/CtfPB8alO+ZWPDafJbgs8AEwN3ycFnV8rgU+JLjD8G3g6HTH3UrHZp/wO88Lv7/OnbrHpxtBEto7YlmHPG8IEve1QCVBL8aVQD7wH+Dj8Llv2LYAeC7is/V+P7W3R5zj8wlBXV3t7507o49PvJ/B9vSIc2weDH+ffECQTAzuiOdOrGMTLr+v9vdMRNuOdt7E+9udsb93NCOWiIiIiGQ8lQeIiIiISMZT0ioiIiIiGU9Jq4iIiIhkPCWtIiIiIpLxlLSKiIiISMZT0ioi0gGZ2QozeyXdcYiIJEpJq4iIiIhkPCWtIiIiIpLxlLSKiIiISMZT0ioikiRm1sXMfmBmH5pZuZmVmtnTZjY2qt2xZuZmdrmZXWdmS8L2S8zsujjb/ryZvWBmW82szMzeN7Mr47Tdz8zuNbM1ZlZhZiVm9qSZHR6j7QFm9qyZbQ+3/ZiZDUrOERERSZ5O6Q5ARKQ9MLMc4N/A0QTzvv8R6A18DXjDzD7v7rOiPnYdMAj4C7AduAD4vZn1dfefRGz7C8ATwDrg12HbycBdZraPu/8wou04gvnCc4C7gQVAX+CYMLbZEfsvBF4Jtz0FGA18HegFnNyyIyIiklzm7umOQUSkzTOz64HfAKe4+8yI5b0IEsdl7n5suOxY4GVgB3Cgu68Jl3cGXgfGAiPcfY2ZZQPLCBLgg9y9JKLty8AE4AB3/9jMDJgP7Acc4e4fRMWY5e414esVwHDgy+7+j4g2fwKuCeP6KGkHSESkhVQeICKSHBcDHwGzzaxf7QPoDLwAfNbMcqM+83Btwgrg7hXAbwmugn0hXHw4MAy4pzZhjWg7leD3+Fnh4jHAwcC90Qlr+JmaqEUlkQlr6KXweb/Gv7KISOtReYCISHIcCOQCGxto0w9YHfF+UYw2C8PnfcLnEeHzhzHaLohqOzJ8ntNgpHsti7Fsc/icn+A2RERahZJWEZHkqL00/90G2kQntLHqs6yR943FEG+7sVQnsC0RkYygpFVEJDk+BvoDL8W4DB/PQTGWHRg+1/aCLg2fD27g87VtF4fPY2O0FRFp01TTKiKSHA8QjAQQs6fVzAbGWHyRmQ2JaNMZuJ6gB/SZcPH7wCrgisihqMLRCqYQ9Ko+GS6eR1BG8BUzq5fkhjdqiYi0SeppFRFJjtuBk4CpZnY8wQ1N2whuojoBKAeOi/rMEuAdM7uTYBirC4HxwC3uvhrA3avN7FqCYaneM7NpYdsvE4wc8At3/zhs62Z2BcGQV++aWe2QV3kEQ179G/hDar6+iEhqKWkVEUkCd680s9MJhou6BKgdZ7UEeBe4P8bH/kAwJup1BMntKuA77n571LafNrMTgJsIelc7E9zE9TV3vyuq7XtmNh74EXA+cDWwKYzhjSR8VRGRtNA4rSIirSxinNYr3P2+tAYjItJGqKZVRERERDKeklYRERERyXhKWkVEREQk46mmVUREREQynnpaRURERCTjKWkVERERkYynpFVEREREMp6SVhERERHJeEpaRURERCTjKWkVERERkYz3/wFkF+1mOtYkJwAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 720x576 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplot(2, 1, 1)\n",
    "plt.title('Training loss',fontsize=18)\n",
    "plt.plot(trainer.loss_history, 'o')\n",
    "plt.xlabel('iteration',fontsize=18)\n",
    "plt.ylabel('loss',fontsize=18)\n",
    "\n",
    "plt.subplot(2, 1, 2)\n",
    "plt.subplots_adjust(left=0.08, right=0.95, wspace=0.25, hspace=0.3)\n",
    "plt.title('train accuracy VS val accuracy',fontsize=18)\n",
    "plt.plot(trainer.train_acc_history, '-o')\n",
    "plt.plot(trainer.val_acc_history, '-*')\n",
    "plt.legend(['train', 'val'], loc='upper left')\n",
    "plt.xlabel('epoch',fontsize=18)\n",
    "plt.ylabel('accuracy',fontsize=18)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0af11fef",
   "metadata": {},
   "source": [
    "## 空间批量归一化\n",
    "\n",
    "BN算法是一种非常高效的技术，大大加快的网络训练。但BN算法通常使用在全连接网络，因此在卷积网络中会有一点修改，我们将其称为\"spatial batch normalization.\"\n",
    "\n",
    "正常的BN接收(N,D)数据，现在我们需要将BN算法调整为接受(N,C,H,W)数据。\n",
    "由于之前我们已经编写好了BN算法，你只需要将图片数据重塑成(N,D)然后调用我们实现好了的BN算法，之后再将其输出结果重塑回(N,C,H,W)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac3e81ab",
   "metadata": {},
   "source": [
    "### SBN前向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "f1960f58",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "使用BN之前:\n",
      "  数据形状:  (2, 3, 4, 5)\n",
      "  均值:  [ 9.63110549 10.5177553   9.05513499]\n",
      "  标准差:  [4.35307203 3.91791992 3.81445076]\n",
      "使用BN后:\n",
      "  输出数据形状:  (2, 3, 4, 5)\n",
      "  均值:  [ 9.99200722e-17 -8.14712880e-16 -6.93889390e-16]\n",
      "  标准差:  [0.99999974 0.99999967 0.99999966]\n",
      "在BN后使用(gamma, beta)进行缩放:\n",
      "  输出数据形状:  (2, 3, 4, 5)\n",
      "  均值:  [6. 7. 8.]\n",
      "  标准差:  [2.99999921 3.9999987  4.99999828]\n"
     ]
    }
   ],
   "source": [
    "#训练阶段：SBN前向传播\n",
    "N, C, H, W = 2, 3, 4, 5\n",
    "x = 4 * np.random.randn(N, C, H, W) + 10\n",
    "\n",
    "print('使用BN之前:')\n",
    "print('  数据形状: ', x.shape)\n",
    "print('  均值: ', x.mean(axis=(0, 2, 3)))\n",
    "print('  标准差: ', x.std(axis=(0, 2, 3)))\n",
    "\n",
    "\n",
    "gamma, beta = np.ones(C), np.zeros(C)\n",
    "bn_param = {'mode': 'train'}\n",
    "out, _ = spatial_batchnorm_forward(x, gamma, beta, bn_param)\n",
    "print('使用BN后:')\n",
    "print('  输出数据形状: ', out.shape)\n",
    "print('  均值: ', out.mean(axis=(0, 2, 3)))\n",
    "print('  标准差: ', out.std(axis=(0, 2, 3)))\n",
    "\n",
    "gamma, beta = np.asarray([3, 4, 5]), np.asarray([6, 7, 8])\n",
    "out, _ = spatial_batchnorm_forward(x, gamma, beta, bn_param)\n",
    "print('在BN后使用(gamma, beta)进行缩放:')\n",
    "print('  输出数据形状: ', out.shape)\n",
    "print('  均值: ', out.mean(axis=(0, 2, 3)))\n",
    "print('  标准差: ', out.std(axis=(0, 2, 3)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "5ba8de3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  均值:  [0.02755273 0.03510479 0.04400864 0.02100596]\n",
      "  标准差:  [1.03484001 0.98419223 1.01060769 1.01369932]\n"
     ]
    }
   ],
   "source": [
    "#测试阶段：SBN前向传播\n",
    "N, C, H, W = 10, 4, 11, 12\n",
    "\n",
    "bn_param = {'mode': 'train'}\n",
    "gamma = np.ones(C)\n",
    "beta = np.zeros(C)\n",
    "\n",
    "for t in range(50):\n",
    "    x = 2.3 * np.random.randn(N, C, H, W) + 13\n",
    "    spatial_batchnorm_forward(x, gamma, beta, bn_param)\n",
    "bn_param['mode'] = 'test'\n",
    "x = 2.3 * np.random.randn(N, C, H, W) + 13\n",
    "a_norm, _ = spatial_batchnorm_forward(x, gamma, beta, bn_param)\n",
    "\n",
    "print('  均值: ', a_norm.mean(axis=(0, 2, 3)))\n",
    "print('  标准差: ', a_norm.std(axis=(0, 2, 3)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f975619",
   "metadata": {},
   "source": [
    "### SBN反向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "89ff9168",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dx 误差:  1.5952312994552347e-08\n",
      "dgamma 误差:  4.433504952292138e-12\n",
      "dbeta 误差:  4.0383999407118856e-12\n"
     ]
    }
   ],
   "source": [
    "N, C, H, W = 2, 3, 4, 5\n",
    "x = 5 * np.random.randn(N, C, H, W) + 12\n",
    "gamma = np.random.randn(C)\n",
    "beta = np.random.randn(C)\n",
    "dout = np.random.randn(N, C, H, W)\n",
    "\n",
    "bn_param = {'mode': 'train'}\n",
    "fx = lambda x: spatial_batchnorm_forward(x, gamma, beta, bn_param)[0]\n",
    "fg = lambda a: spatial_batchnorm_forward(x, gamma, beta, bn_param)[0]\n",
    "fb = lambda b: spatial_batchnorm_forward(x, gamma, beta, bn_param)[0]\n",
    "\n",
    "dx_num = eval_numerical_gradient_array(fx, x, dout)\n",
    "da_num = eval_numerical_gradient_array(fg, gamma, dout)\n",
    "db_num = eval_numerical_gradient_array(fb, beta, dout)\n",
    "\n",
    "_, cache = spatial_batchnorm_forward(x, gamma, beta, bn_param)\n",
    "dx, dgamma, dbeta = spatial_batchnorm_backward(dout, cache)\n",
    "print('dx 误差: ', rel_error(dx_num, dx))\n",
    "print('dgamma 误差: ', rel_error(da_num, dgamma))\n",
    "print('dbeta 误差: ', rel_error(db_num, dbeta))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ff3f1cb",
   "metadata": {},
   "source": [
    "## 训练卷积网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f6e8fea5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(迭代 1 / 9800) 损失值: 2.303339\n",
      "(周期 0 / 10) 训练精度: 0.101000; 验证精度: 0.119000\n",
      "(迭代 3 / 9800) 损失值: 2.334345\n",
      "(迭代 5 / 9800) 损失值: 2.399291\n",
      "(迭代 7 / 9800) 损失值: 2.316727\n",
      "(迭代 9 / 9800) 损失值: 2.278965\n",
      "(迭代 11 / 9800) 损失值: 2.239781\n",
      "(迭代 13 / 9800) 损失值: 2.089464\n",
      "(迭代 15 / 9800) 损失值: 2.169326\n",
      "(迭代 17 / 9800) 损失值: 2.117073\n",
      "(迭代 19 / 9800) 损失值: 1.897549\n",
      "(迭代 21 / 9800) 损失值: 2.044792\n",
      "(迭代 23 / 9800) 损失值: 2.229730\n",
      "(迭代 25 / 9800) 损失值: 2.279604\n",
      "(迭代 27 / 9800) 损失值: 2.222372\n",
      "(迭代 29 / 9800) 损失值: 2.022023\n",
      "(迭代 31 / 9800) 损失值: 1.893715\n",
      "(迭代 33 / 9800) 损失值: 1.886617\n",
      "(迭代 35 / 9800) 损失值: 2.101612\n",
      "(迭代 37 / 9800) 损失值: 2.048918\n",
      "(迭代 39 / 9800) 损失值: 2.114011\n",
      "(迭代 41 / 9800) 损失值: 2.034655\n",
      "(迭代 43 / 9800) 损失值: 1.696890\n",
      "(迭代 45 / 9800) 损失值: 2.004295\n",
      "(迭代 47 / 9800) 损失值: 1.894582\n",
      "(迭代 49 / 9800) 损失值: 1.915542\n",
      "(迭代 51 / 9800) 损失值: 2.002454\n",
      "(迭代 53 / 9800) 损失值: 2.029914\n",
      "(迭代 55 / 9800) 损失值: 1.920715\n",
      "(迭代 57 / 9800) 损失值: 1.811339\n",
      "(迭代 59 / 9800) 损失值: 1.879020\n",
      "(迭代 61 / 9800) 损失值: 1.987333\n",
      "(迭代 63 / 9800) 损失值: 1.847146\n",
      "(迭代 65 / 9800) 损失值: 1.911756\n",
      "(迭代 67 / 9800) 损失值: 1.860287\n",
      "(迭代 69 / 9800) 损失值: 1.977371\n",
      "(迭代 71 / 9800) 损失值: 1.936084\n",
      "(迭代 73 / 9800) 损失值: 1.639608\n",
      "(迭代 75 / 9800) 损失值: 1.990092\n",
      "(迭代 77 / 9800) 损失值: 1.901660\n",
      "(迭代 79 / 9800) 损失值: 2.005168\n",
      "(迭代 81 / 9800) 损失值: 1.889691\n",
      "(迭代 83 / 9800) 损失值: 2.022044\n",
      "(迭代 85 / 9800) 损失值: 1.931332\n",
      "(迭代 87 / 9800) 损失值: 1.972134\n",
      "(迭代 89 / 9800) 损失值: 1.777364\n",
      "(迭代 91 / 9800) 损失值: 1.780737\n",
      "(迭代 93 / 9800) 损失值: 1.963789\n",
      "(迭代 95 / 9800) 损失值: 1.927447\n",
      "(迭代 97 / 9800) 损失值: 1.960523\n",
      "(迭代 99 / 9800) 损失值: 1.914877\n",
      "(迭代 101 / 9800) 损失值: 1.716645\n",
      "(迭代 103 / 9800) 损失值: 1.765718\n",
      "(迭代 105 / 9800) 损失值: 2.122341\n",
      "(迭代 107 / 9800) 损失值: 1.849172\n",
      "(迭代 109 / 9800) 损失值: 2.020525\n",
      "(迭代 111 / 9800) 损失值: 1.915613\n",
      "(迭代 113 / 9800) 损失值: 1.808912\n",
      "(迭代 115 / 9800) 损失值: 1.846372\n",
      "(迭代 117 / 9800) 损失值: 1.891024\n",
      "(迭代 119 / 9800) 损失值: 1.569039\n",
      "(迭代 121 / 9800) 损失值: 1.637754\n",
      "(迭代 123 / 9800) 损失值: 1.698949\n",
      "(迭代 125 / 9800) 损失值: 1.985955\n",
      "(迭代 127 / 9800) 损失值: 1.673864\n",
      "(迭代 129 / 9800) 损失值: 1.826067\n",
      "(迭代 131 / 9800) 损失值: 1.815124\n",
      "(迭代 133 / 9800) 损失值: 1.866925\n",
      "(迭代 135 / 9800) 损失值: 1.940567\n",
      "(迭代 137 / 9800) 损失值: 1.884095\n",
      "(迭代 139 / 9800) 损失值: 1.849993\n",
      "(迭代 141 / 9800) 损失值: 1.941039\n",
      "(迭代 143 / 9800) 损失值: 1.857288\n",
      "(迭代 145 / 9800) 损失值: 1.730857\n",
      "(迭代 147 / 9800) 损失值: 2.105659\n",
      "(迭代 149 / 9800) 损失值: 1.795795\n",
      "(迭代 151 / 9800) 损失值: 2.174046\n",
      "(迭代 153 / 9800) 损失值: 2.019538\n",
      "(迭代 155 / 9800) 损失值: 1.732757\n",
      "(迭代 157 / 9800) 损失值: 1.670984\n",
      "(迭代 159 / 9800) 损失值: 1.592464\n",
      "(迭代 161 / 9800) 损失值: 1.813895\n",
      "(迭代 163 / 9800) 损失值: 1.639067\n",
      "(迭代 165 / 9800) 损失值: 1.587938\n",
      "(迭代 167 / 9800) 损失值: 1.799356\n",
      "(迭代 169 / 9800) 损失值: 1.750778\n",
      "(迭代 171 / 9800) 损失值: 2.134175\n",
      "(迭代 173 / 9800) 损失值: 1.817605\n",
      "(迭代 175 / 9800) 损失值: 1.787242\n",
      "(迭代 177 / 9800) 损失值: 1.668361\n",
      "(迭代 179 / 9800) 损失值: 1.793518\n",
      "(迭代 181 / 9800) 损失值: 1.883326\n",
      "(迭代 183 / 9800) 损失值: 1.633063\n",
      "(迭代 185 / 9800) 损失值: 1.519529\n",
      "(迭代 187 / 9800) 损失值: 1.887889\n",
      "(迭代 189 / 9800) 损失值: 1.686772\n",
      "(迭代 191 / 9800) 损失值: 1.774219\n",
      "(迭代 193 / 9800) 损失值: 1.757321\n",
      "(迭代 195 / 9800) 损失值: 1.570355\n",
      "(迭代 197 / 9800) 损失值: 1.778653\n",
      "(迭代 199 / 9800) 损失值: 1.653307\n",
      "(迭代 201 / 9800) 损失值: 2.068612\n",
      "(迭代 203 / 9800) 损失值: 1.639195\n",
      "(迭代 205 / 9800) 损失值: 1.680369\n",
      "(迭代 207 / 9800) 损失值: 1.730560\n",
      "(迭代 209 / 9800) 损失值: 1.687998\n",
      "(迭代 211 / 9800) 损失值: 1.747248\n",
      "(迭代 213 / 9800) 损失值: 1.740587\n",
      "(迭代 215 / 9800) 损失值: 1.891618\n",
      "(迭代 217 / 9800) 损失值: 1.933578\n",
      "(迭代 219 / 9800) 损失值: 1.585058\n",
      "(迭代 221 / 9800) 损失值: 1.834793\n",
      "(迭代 223 / 9800) 损失值: 1.720518\n",
      "(迭代 225 / 9800) 损失值: 1.623339\n",
      "(迭代 227 / 9800) 损失值: 1.987607\n",
      "(迭代 229 / 9800) 损失值: 1.597042\n",
      "(迭代 231 / 9800) 损失值: 1.533966\n",
      "(迭代 233 / 9800) 损失值: 1.698381\n",
      "(迭代 235 / 9800) 损失值: 1.701220\n",
      "(迭代 237 / 9800) 损失值: 1.652729\n",
      "(迭代 239 / 9800) 损失值: 2.110093\n",
      "(迭代 241 / 9800) 损失值: 1.734058\n",
      "(迭代 243 / 9800) 损失值: 1.702769\n",
      "(迭代 245 / 9800) 损失值: 1.711049\n",
      "(迭代 247 / 9800) 损失值: 1.917496\n",
      "(迭代 249 / 9800) 损失值: 1.549907\n",
      "(迭代 251 / 9800) 损失值: 1.843834\n",
      "(迭代 253 / 9800) 损失值: 1.738030\n"
     ]
    }
   ],
   "source": [
    "model = ThreeLayerConvNet(weight_scale=0.001, hidden_dim=200, reg=0.001)\n",
    "\n",
    "trainer = Trainer(model, data,\n",
    "                num_epochs=10, batch_size=50,\n",
    "                update_rule='adam',\n",
    "                updater_config={\n",
    "                  'learning_rate': 1e-3,\n",
    "                },\n",
    "                verbose=True, print_every=2)\n",
    "trainer.train()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d2e3ca6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
